summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md16
-rw-r--r--man/man1/fzf.160
-rw-r--r--src/actiontype_string.go202
-rw-r--r--src/options.go244
-rw-r--r--src/terminal.go203
-rw-r--r--src/tui/dummy.go3
-rw-r--r--src/tui/light.go81
-rw-r--r--src/tui/light_unix.go2
-rw-r--r--src/tui/light_windows.go2
-rw-r--r--src/tui/tcell.go53
-rw-r--r--src/tui/tui.go83
11 files changed, 620 insertions, 329 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc8c03a9..620d0dcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
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
------
- You can now resize the preview window by dragging the border
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index b1601c70..0b0654b4 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -552,33 +552,37 @@ color mappings.
\fBbw \fRNo colors (equivalent to \fB\-\-no\-color\fR)
.B COLOR NAMES:
- \fBfg \fRText
- \fBselected\-fg \fRSelected line text
- \fBpreview\-fg \fRPreview window text
- \fBbg \fRBackground
- \fBselected\-bg \fRSelected line background
- \fBpreview\-bg \fRPreview window 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
- \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)
- \fBscrollbar \fRScrollbar
- \fBpreview\-border \fRBorder around the preview window (\fB\-\-preview\fR)
- \fBpreview\-scrollbar \fRScrollbar
- \fBseparator \fRHorizontal separator on info line
- \fBlabel \fRBorder label (\fB\-\-border\-label\fR and \fB\-\-preview\-label\fR)
- \fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
- \fBprompt \fRPrompt
- \fBpointer \fRPointer to the current line
- \fBmarker \fRMulti\-select marker
- \fBspinner \fRStreaming input indicator
- \fBheader \fRHeader
+ \fBfg \fRText
+ \fBlist\-fg \fRText in the list section
+ \fBselected\-fg \fRSelected line text
+ \fBpreview\-fg \fRPreview window text
+ \fBbg \fRBackground
+ \fBlist\-bg \fRBackground in the list section
+ \fBselected\-bg \fRSelected line background
+ \fBpreview\-bg \fRPreview window 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
+ \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)
+ \fBlist\-border \fRBorder around the list section (\fB\-\-list\-border\fR)
+ \fBscrollbar \fRScrollbar
+ \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)
+ \fBlist\-label \fRBorder label of the list section (\fB\-\-list\-label\fR)
+ \fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
+ \fBprompt \fRPrompt
+ \fBpointer \fRPointer to the current line
+ \fBmarker \fRMulti\-select marker
+ \fBspinner \fRStreaming input indicator
+ \fBheader \fRHeader
.B ANSI COLORS:
\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)
\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\-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)
\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\-border\-label(...)\fR (transform border label 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\-prompt(...)\fR (transform prompt string using an external command)
\fBtransform\-query(...)\fR (transform query string using an external command)
diff --git a/src/actiontype_string.go b/src/actiontype_string.go
index 8cbd76e8..675a7777 100644
--- a/src/actiontype_string.go
+++ b/src/actiontype_string.go
@@ -25,109 +25,111 @@ func _() {
_ = x[actBackwardWord-14]
_ = x[actCancel-15]
_ = x[actChangeBorderLabel-16]
- _ = x[actChangeHeader-17]
- _ = x[actChangeMulti-18]
- _ = x[actChangePreviewLabel-19]
- _ = x[actChangePrompt-20]
- _ = x[actChangeQuery-21]
- _ = x[actClearScreen-22]
- _ = x[actClearQuery-23]
- _ = x[actClearSelection-24]
- _ = x[actClose-25]
- _ = x[actDeleteChar-26]
- _ = x[actDeleteCharEof-27]
- _ = x[actEndOfLine-28]
- _ = x[actFatal-29]
- _ = x[actForwardChar-30]
- _ = x[actForwardWord-31]
- _ = x[actKillLine-32]
- _ = x[actKillWord-33]
- _ = x[actUnixLineDiscard-34]
- _ = x[actUnixWordRubout-35]
- _ = x[actYank-36]
- _ = x[actBackwardKillWord-37]
- _ = x[actSelectAll-38]
- _ = x[actDeselectAll-39]
- _ = x[actToggle-40]
- _ = x[actToggleSearch-41]
- _ = x[actToggleAll-42]
- _ = x[actToggleDown-43]
- _ = x[actToggleUp-44]
- _ = x[actToggleIn-45]
- _ = x[actToggleOut-46]
- _ = x[actToggleTrack-47]
- _ = x[actToggleTrackCurrent-48]
- _ = x[actToggleHeader-49]
- _ = x[actToggleWrap-50]
- _ = x[actToggleMultiLine-51]
- _ = x[actToggleHscroll-52]
- _ = x[actTrackCurrent-53]
- _ = x[actUntrackCurrent-54]
- _ = x[actDown-55]
- _ = x[actUp-56]
- _ = x[actPageUp-57]
- _ = x[actPageDown-58]
- _ = x[actPosition-59]
- _ = x[actHalfPageUp-60]
- _ = x[actHalfPageDown-61]
- _ = x[actOffsetUp-62]
- _ = x[actOffsetDown-63]
- _ = x[actOffsetMiddle-64]
- _ = x[actJump-65]
- _ = x[actJumpAccept-66]
- _ = x[actPrintQuery-67]
- _ = x[actRefreshPreview-68]
- _ = x[actReplaceQuery-69]
- _ = x[actToggleSort-70]
- _ = x[actShowPreview-71]
- _ = x[actHidePreview-72]
- _ = x[actTogglePreview-73]
- _ = x[actTogglePreviewWrap-74]
- _ = x[actTransform-75]
- _ = x[actTransformBorderLabel-76]
- _ = x[actTransformHeader-77]
- _ = x[actTransformPreviewLabel-78]
- _ = x[actTransformPrompt-79]
- _ = x[actTransformQuery-80]
- _ = x[actPreview-81]
- _ = x[actChangePreview-82]
- _ = x[actChangePreviewWindow-83]
- _ = x[actPreviewTop-84]
- _ = x[actPreviewBottom-85]
- _ = x[actPreviewUp-86]
- _ = x[actPreviewDown-87]
- _ = x[actPreviewPageUp-88]
- _ = x[actPreviewPageDown-89]
- _ = x[actPreviewHalfPageUp-90]
- _ = x[actPreviewHalfPageDown-91]
- _ = x[actPrevHistory-92]
- _ = x[actPrevSelected-93]
- _ = x[actPrint-94]
- _ = x[actPut-95]
- _ = x[actNextHistory-96]
- _ = x[actNextSelected-97]
- _ = x[actExecute-98]
- _ = x[actExecuteSilent-99]
- _ = x[actExecuteMulti-100]
- _ = x[actSigStop-101]
- _ = x[actFirst-102]
- _ = x[actLast-103]
- _ = x[actReload-104]
- _ = x[actReloadSync-105]
- _ = x[actDisableSearch-106]
- _ = x[actEnableSearch-107]
- _ = x[actSelect-108]
- _ = x[actDeselect-109]
- _ = x[actUnbind-110]
- _ = x[actRebind-111]
- _ = x[actBecome-112]
- _ = x[actShowHeader-113]
- _ = x[actHideHeader-114]
+ _ = 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]
}
-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 {
if i < 0 || i >= actionType(len(_actionType_index)-1) {
diff --git a/src/options.go b/src/options.go
index c8e72ff1..bc0ddeb3 100644
--- a/src/options.go
+++ b/src/options.go
@@ -87,6 +87,14 @@ Usage: fzf [options]
[POSITIVE_INTEGER: columns from left|
NEGATIVE_INTEGER: columns from right][:bottom]
(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)
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--info=STYLE Finder info style
@@ -436,100 +444,102 @@ type walkerOpts struct {
// Options stores the values of command-line options
type Options struct {
- Input chan string
- Output chan string
- NoWinpty bool
- Tmux *tmuxOptions
- ForceTtyIn bool
- ProxyScript string
- Bash bool
- Zsh bool
- Fish bool
- Man bool
- Fuzzy bool
- FuzzyAlgo algo.Algo
- Scheme string
- Extended bool
- Phony bool
- Case Case
- Normalize bool
- Nth []Range
- WithNth []Range
- Delimiter Delimiter
- Sort int
- Track trackOption
- Tac bool
- Tail int
- Criteria []criterion
- Multi int
- Ansi bool
- Mouse bool
- Theme *tui.ColorTheme
- Black bool
- Bold bool
- Height heightSpec
- MinHeight int
- Layout layoutType
- Cycle bool
- Wrap bool
- WrapSign *string
- MultiLine bool
- CursorLine bool
- KeepRight bool
- Hscroll bool
- HscrollOff int
- ScrollOff int
- FileWord bool
- InfoStyle infoStyle
- InfoPrefix string
- InfoCommand string
- Separator *string
- JumpLabels string
- Prompt string
- Pointer *string
- Marker *string
- MarkerMulti *[3]string
- Query string
- Select1 bool
- Exit0 bool
- Filter *string
- ToggleSort bool
- Expect map[tui.Event]string
- Keymap map[tui.Event][]*action
- Preview previewOpts
- PrintQuery bool
- ReadZero bool
- Printer func(string)
- PrintSep string
- Sync bool
- History *History
- Header []string
- HeaderLines int
- HeaderFirst bool
- Gap int
- Ellipsis *string
- Scrollbar *string
- Margin [4]sizeSpec
- Padding [4]sizeSpec
- BorderShape tui.BorderShape
- BorderLabel labelOpts
- PreviewLabel labelOpts
- Unicode bool
- Ambidouble bool
- Tabstop int
- WithShell string
- ListenAddr *listenAddress
- Unsafe bool
- ClearOnExit bool
- WalkerOpts walkerOpts
- WalkerRoot []string
- WalkerSkip []string
- Version bool
- Help bool
- CPUProfile string
- MEMProfile string
- BlockProfile string
- MutexProfile string
+ Input chan string
+ Output chan string
+ NoWinpty bool
+ Tmux *tmuxOptions
+ ForceTtyIn bool
+ ProxyScript string
+ Bash bool
+ Zsh bool
+ Fish bool
+ Man bool
+ Fuzzy bool
+ FuzzyAlgo algo.Algo
+ Scheme string
+ Extended bool
+ Phony bool
+ Case Case
+ Normalize bool
+ Nth []Range
+ WithNth []Range
+ Delimiter Delimiter
+ Sort int
+ Track trackOption
+ Tac bool
+ Tail int
+ Criteria []criterion
+ Multi int
+ Ansi bool
+ Mouse bool
+ Theme *tui.ColorTheme
+ Black bool
+ Bold bool
+ Height heightSpec
+ MinHeight int
+ Layout layoutType
+ Cycle bool
+ Wrap bool
+ WrapSign *string
+ MultiLine bool
+ CursorLine bool
+ KeepRight bool
+ Hscroll bool
+ HscrollOff int
+ ScrollOff int
+ FileWord bool
+ InfoStyle infoStyle
+ InfoPrefix string
+ InfoCommand string
+ Separator *string
+ JumpLabels string
+ Prompt string
+ Pointer *string
+ Marker *string
+ MarkerMulti *[3]string
+ Query string
+ Select1 bool
+ Exit0 bool
+ Filter *string
+ ToggleSort bool
+ Expect map[tui.Event]string
+ Keymap map[tui.Event][]*action
+ Preview previewOpts
+ PrintQuery bool
+ ReadZero bool
+ Printer func(string)
+ PrintSep string
+ Sync bool
+ History *History
+ Header []string
+ HeaderLines int
+ HeaderFirst bool
+ Gap int
+ Ellipsis *string
+ Scrollbar *string
+ Margin [4]sizeSpec
+ Padding [4]sizeSpec
+ BorderShape tui.BorderShape
+ ListBorderShape tui.BorderShape
+ BorderLabel labelOpts
+ ListLabel labelOpts
+ PreviewLabel labelOpts
+ Unicode bool
+ Ambidouble bool
+ Tabstop int
+ WithShell string
+ ListenAddr *listenAddress
+ Unsafe bool
+ ClearOnExit bool
+ WalkerOpts walkerOpts
+ WalkerRoot []string
+ WalkerSkip []string
+ Version bool
+ Help bool
+ CPUProfile string
+ MEMProfile string
+ BlockProfile string
+ MutexProfile string
}
func filterNonEmpty(input []string) []string {
@@ -1166,6 +1176,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.Fg)
case "bg":
mergeAttr(&theme.Bg)
+ case "list-fg":
+ mergeAttr(&theme.ListFg)
+ case "list-bg":
+ mergeAttr(&theme.ListBg)
case "preview-fg":
mergeAttr(&theme.PreviewFg)
case "preview-bg":
@@ -1198,6 +1212,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.PreviewScrollbar)
case "label":
mergeAttr(&theme.BorderLabel)
+ case "list-label":
+ mergeAttr(&theme.ListLabel)
+ case "list-border":
+ mergeAttr(&theme.ListBorder)
case "preview-label":
mergeAttr(&theme.PreviewLabel)
case "prompt":
@@ -1265,7 +1283,7 @@ const (
func init() {
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("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@@ -1621,6 +1639,8 @@ func isExecuteAction(str string) actionType {
return actRebind
case "preview":
return actPreview
+ case "change-list-label":
+ return actChangeListLabel
case "change-border-label":
return actChangeBorderLabel
case "change-header":
@@ -1651,6 +1671,8 @@ func isExecuteAction(str string) actionType {
return actPut
case "transform":
return actTransform
+ case "transform-list-label":
+ return actTransformListLabel
case "transform-border-label":
return actTransformBorderLabel
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 {
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":
opts.BorderLabel.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 {
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 {
opts.BorderLabel.label = value
} else if match, value := optString(arg, "--border-label-pos="); match {
@@ -2864,6 +2914,10 @@ func postProcessOptions(opts *Options) error {
opts.BorderShape = tui.BorderNone
}
+ if opts.ListBorderShape == tui.BorderUndefined {
+ opts.ListBorderShape = tui.BorderNone
+ }
+
if opts.Pointer == nil {
defaultPointer := "▌"
if !opts.Unicode {
diff --git a/src/terminal.go b/src/terminal.go
index 9b805123..23227a28 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -297,11 +297,16 @@ type Terminal struct {
listener net.Listener
listenUnsafe bool
borderShape tui.BorderShape
+ listBorderShape tui.BorderShape
+ listLabel labelPrinter
+ listLabelLen int
+ listLabelOpts labelOpts
cleanExit bool
executor *util.Executor
paused bool
border tui.Window
window tui.Window
+ wborder tui.Window
pborder tui.Window
pwindow tui.Window
borderWidth int
@@ -389,6 +394,7 @@ const (
reqReinit
reqFullRedraw
reqResize
+ reqRedrawListLabel
reqRedrawBorderLabel
reqRedrawPreviewLabel
reqClose
@@ -429,6 +435,7 @@ const (
actBackwardWord
actCancel
actChangeBorderLabel
+ actChangeListLabel
actChangeHeader
actChangeMulti
actChangePreviewLabel
@@ -489,6 +496,7 @@ const (
actTogglePreviewWrap
actTransform
actTransformBorderLabel
+ actTransformListLabel
actTransformHeader
actTransformPreviewLabel
actTransformPrompt
@@ -823,7 +831,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
listenAddr: opts.ListenAddr,
listenUnsafe: opts.Unsafe,
borderShape: opts.BorderShape,
+ listBorderShape: opts.ListBorderShape,
borderWidth: 1,
+ listLabel: nil,
+ listLabelOpts: opts.ListLabel,
borderLabel: nil,
borderLabelOpts: opts.BorderLabel,
previewLabel: nil,
@@ -880,10 +891,15 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
executing: util.NewAtomicBool(false),
lastAction: actStart,
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)
// Pre-calculated empty pointer and marker signs
t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
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.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
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_PREVIEW_LABEL="+t.previewLabelOpts.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_MATCH_COUNT=%d", t.merger.Length()))
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
loc := whiteSuffix.FindStringIndex(trimmed)
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 {
lastColor := (*item.colors)[len(*item.colors)-1]
if lastColor.offset[1] < int32(loc[1]) {
@@ -1546,6 +1563,9 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if t.window != nil {
t.window = nil
}
+ if t.wborder != nil {
+ t.wborder = nil
+ }
if t.pborder != nil {
t.pborder = nil
}
@@ -1572,10 +1592,10 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
offsets[1] -= 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(
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
@@ -1585,6 +1605,33 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
width -= paddingInt[1] + paddingInt[3]
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.areaColumns = width
@@ -1592,6 +1639,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
if forcePreview || t.needPreviewWindow() {
var resizePreviewWindows func(previewOpts *previewOpts)
+ stickToRight := false
resizePreviewWindows = func(previewOpts *previewOpts) {
t.activePreviewOpts = previewOpts
if previewOpts.size.size == 0 {
@@ -1602,7 +1650,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
pwidth := w
pheight := h
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)
pheight -= borderLines(previewOpts.border)
if previewOpts.border.HasLeft() {
@@ -1617,7 +1665,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
pwidth = util.Max(0, pwidth)
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 {
t.pwindow.Erase()
}
@@ -1647,16 +1695,25 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
return
}
if previewOpts.position == posUp {
+ innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
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)
} else {
+ innerBorderFn(marginInt[0], marginInt[3], width, height-pheight)
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)
}
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 {
t.activePreviewOpts = previewOpts.alternative
if forcePreview {
@@ -1675,52 +1732,83 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
if previewOpts.position == posLeft {
// Put scrollbar closer to the right border for consistent look
- if t.borderShape.HasRight() {
- width++
+ if t.borderShape.HasRight() && !hasListBorder {
+ innerWidth++
}
// Add a 1-column margin between the preview window and the main window
+ m := 0
+ if !hasListBorder {
+ m = 1
+ }
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
// fzf --bind 'space:preview(seq 100)' --preview-window left,1
- for y := 0; y < height; y++ {
- t.window.Move(y, -1)
- t.window.Print(" ")
+ if !hasListBorder {
+ for y := 0; y < innerHeight; y++ {
+ t.window.Move(y, -1)
+ t.window.Print(" ")
+ }
}
+ innerBorderFn(marginInt[0], marginInt[3]+pwidth, width-pwidth, height)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
} else {
// 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++
}
+ innerBorderFn(marginInt[0], marginInt[3], width-pwidth, height)
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
createPreviewWindow(marginInt[0], x, pwidth, height)
}
}
}
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 {
t.activePreviewOpts = &t.previewOpts
}
// Without preview window
if t.window == nil {
- if t.borderShape.HasRight() {
+ if t.borderShape.HasRight() && !hasListBorder {
// Put scrollbar closer to the right border for consistent look
+ innerWidth++
width++
}
+ innerBorderFn(marginInt[0], marginInt[3], width, height)
t.window = t.tui.NewWindow(
- marginInt[0],
- marginInt[3],
- width,
- height, false, noBorder)
+ innerMarginInt[0],
+ innerMarginInt[3],
+ innerWidth,
+ innerHeight, tui.WindowList, 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)
}
@@ -1839,6 +1927,9 @@ func (t *Terminal) trimMessage(message string, maxWidth int) string {
}
func (t *Terminal) printInfo() {
+ if t.window.Width() <= 1 {
+ return
+ }
pos := 0
line := t.promptLine()
maxHeight := t.window.Height()
@@ -1977,7 +2068,9 @@ func (t *Terminal) printInfo() {
} else {
outputPrinter(t.window, maxWidth-1)
}
- t.window.Print(" ") // Margin
+ if fillLength >= 0 {
+ t.window.Print(" ") // Margin
+ }
return
}
@@ -2115,7 +2208,7 @@ func (t *Terminal) printList() {
func (t *Terminal) printBar(lineNum int, forceRedraw bool, barRange [2]int) bool {
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)
if len(t.scrollbar) > 0 && hasBar {
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 {
preTask := func(marker markerClass) {
+ w := t.window.Width() - t.pointerLen
+ if w < 0 {
+ return
+ }
if len(label) == 0 {
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
} else {
t.window.CPrint(tui.ColCurrentCursor, label)
}
+ if w-t.markerLen < 0 {
+ return
+ }
if selected {
t.window.CPrint(tui.ColCurrentMarker, markerFor(marker))
} 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)
} else {
preTask := func(marker markerClass) {
+ w := t.window.Width() - t.pointerLen
+ if w < 0 {
+ return
+ }
if len(label) == 0 {
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty)
} else {
t.window.CPrint(tui.ColCursor, label)
}
+ if w-t.markerLen < 0 {
+ return
+ }
if selected {
t.window.CPrint(tui.ColMarker, markerFor(marker))
} else {
@@ -2946,7 +3053,7 @@ func (t *Terminal) flush() {
t.placeCursor()
if !t.suppress {
windows := make([]tui.Window, 0, 4)
- if t.borderShape != tui.BorderNone {
+ if t.borderShape.Visible() {
windows = append(windows, t.border)
}
if t.hasPreviewWindow() {
@@ -3855,6 +3962,8 @@ func (t *Terminal) Loop() error {
if t.hasPreviewer() {
t.previewBox.Set(reqPreviewReady, nil)
}
+ case reqRedrawListLabel:
+ t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
case reqRedrawBorderLabel:
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, true)
case reqRedrawPreviewLabel:
@@ -3952,7 +4061,7 @@ func (t *Terminal) Loop() error {
previewDraggingPos := -1
barDragging := false
pbarDragging := false
- pborderDragging := false
+ pborderDragging := -1
wasDown := false
needBarrier := true
@@ -4236,6 +4345,12 @@ func (t *Terminal) Loop() error {
} else {
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:
t.borderLabelOpts.label = a.a
if t.border != nil {
@@ -4253,6 +4368,13 @@ func (t *Terminal) Loop() error {
if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil {
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:
label := t.executeCommand(a.a, false, true, true, true, "")
t.borderLabelOpts.label = label
@@ -4684,7 +4806,7 @@ func (t *Terminal) Loop() error {
if !me.Down {
barDragging = false
pbarDragging = false
- pborderDragging = false
+ pborderDragging = -1
previewDraggingPos = -1
}
@@ -4740,20 +4862,36 @@ func (t *Terminal) Loop() error {
}
// Preview border dragging (resizing)
- if !pborderDragging && clicked && t.hasPreviewWindow() && t.pborder.Enclose(my, mx) {
+ if pborderDragging < 0 && clicked && t.hasPreviewWindow() {
switch t.activePreviewOpts.position {
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:
- 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:
- 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:
- 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 prevSize int
switch t.activePreviewOpts.position {
@@ -4774,6 +4912,7 @@ func (t *Terminal) Loop() error {
offset := mx - t.pborder.Left()
newSize = prevSize - offset
}
+ newSize -= pborderDragging
if newSize < 1 {
newSize = 1
}
diff --git a/src/tui/dummy.go b/src/tui/dummy.go
index 1a761460..a49677c6 100644
--- a/src/tui/dummy.go
+++ b/src/tui/dummy.go
@@ -30,6 +30,7 @@ const (
)
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) Pause(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) 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
}
diff --git a/src/tui/light.go b/src/tui/light.go
index 95984542..f0bb2fdf 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -116,19 +116,19 @@ type LightRenderer struct {
}
type LightWindow struct {
- renderer *LightRenderer
- colored bool
- preview bool
- border BorderStyle
- top int
- left int
- width int
- height int
- posx int
- posy int
- tabstop int
- fg Color
- bg Color
+ renderer *LightRenderer
+ colored bool
+ windowType WindowType
+ border BorderStyle
+ top int
+ left int
+ width int
+ height int
+ posx int
+ posy int
+ tabstop int
+ fg 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) {
@@ -174,7 +174,6 @@ func (r *LightRenderer) Init() error {
return err
}
r.updateTerminalSize()
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
if r.fullscreen {
r.smcup()
@@ -780,27 +779,32 @@ func (r *LightRenderer) MaxY() int {
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{
- renderer: r,
- colored: r.theme.Colored,
- preview: preview,
- border: borderStyle,
- top: top,
- left: left,
- width: width,
- height: height,
- tabstop: r.tabstop,
- fg: colDefault,
- bg: colDefault}
- if preview {
- w.fg = r.theme.PreviewFg.Color
- w.bg = r.theme.PreviewBg.Color
- } else {
+ renderer: r,
+ colored: r.theme.Colored,
+ windowType: windowType,
+ border: borderStyle,
+ top: top,
+ left: left,
+ width: width,
+ height: height,
+ tabstop: r.tabstop,
+ fg: colDefault,
+ bg: colDefault}
+ switch windowType {
+ case WindowBase:
w.fg = r.theme.Fg.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.drawBorder(false)
@@ -845,7 +849,10 @@ func (w *LightWindow) drawBorder(onlyHorizontal bool) {
func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
hw := runeWidth(w.border.top)
@@ -863,7 +870,10 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
func (w *LightWindow) drawBorderVertical(left, right bool) {
vw := runeWidth(w.border.left)
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
for y := 0; y < w.height; y++ {
@@ -883,7 +893,10 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
w.Move(0, 0)
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
hw := runeWidth(w.border.top)
diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go
index c2d5612a..76aac2eb 100644
--- a/src/tui/light_unix.go
+++ b/src/tui/light_unix.go
@@ -18,7 +18,7 @@ func IsLightRendererSupported() bool {
return true
}
-func (r *LightRenderer) defaultTheme() *ColorTheme {
+func (r *LightRenderer) DefaultTheme() *ColorTheme {
if strings.Contains(os.Getenv("TERM"), "256") {
return Dark256
}
diff --git a/src/tui/light_windows.go b/src/tui/light_windows.go
index e2a2bbfe..cf5126ab 100644
--- a/src/tui/light_windows.go
+++ b/src/tui/light_windows.go
@@ -39,7 +39,7 @@ func IsLightRendererSupported() bool {
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:
if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
return Default16
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index a3ce2cb1..92336cd0 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -40,7 +40,7 @@ type Attr int32
type TcellWindow struct {
color bool
- preview bool
+ windowType WindowType
top int
left int
width int
@@ -106,8 +106,12 @@ func (r *FullscreenRenderer) PassThrough(str string) {
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
-func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
- if _screen.Colors() >= 256 {
+func (r *FullscreenRenderer) DefaultTheme() *ColorTheme {
+ s, e := r.getScreen()
+ if e != nil {
+ return Default16
+ }
+ if s.Colors() >= 256 {
return Dark256
}
return Default16
@@ -148,8 +152,19 @@ var (
_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 {
- s, e := tcell.NewScreen()
+ s, e := r.getScreen()
if e != nil {
return e
}
@@ -161,7 +176,6 @@ func (r *FullscreenRenderer) initScreen() error {
} else {
s.DisableMouse()
}
- _screen = s
return nil
}
@@ -174,7 +188,6 @@ func (r *FullscreenRenderer) Init() error {
if err := r.initScreen(); err != nil {
return err
}
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
return nil
}
@@ -537,14 +550,17 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
_screen.Show()
}
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
- normal := ColNormal
- if preview {
- normal = ColPreview
+func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
+ normal := ColBorder
+ switch windowType {
+ case WindowList:
+ normal = ColListBorder
+ case WindowPreview:
+ normal = ColPreviewBorder
}
w := &TcellWindow{
color: r.theme.Colored,
- preview: preview,
+ windowType: windowType,
top: top,
left: left,
width: width,
@@ -564,11 +580,7 @@ func fill(x, y, w, h int, n ColorPair, r rune) {
}
func (w *TcellWindow) Erase() {
- if w.borderStyle.shape.HasLeft() {
- 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, ' ')
- }
+ fill(w.left, w.top, w.width-1, w.height-1, w.normal, ' ')
w.drawBorder(false)
}
@@ -768,10 +780,13 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
var style tcell.Style
if w.color {
- if w.preview {
- style = ColPreviewBorder.style()
- } else {
+ switch w.windowType {
+ case WindowBase:
style = ColBorder.style()
+ case WindowList:
+ style = ColListBorder.style()
+ case WindowPreview:
+ style = ColPreviewBorder.style()
}
} else {
style = w.normal.style()
diff --git a/src/tui/tui.go b/src/tui/tui.go
index 902d13fe..e2a891d0 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -303,6 +303,8 @@ type ColorTheme struct {
Disabled ColorAttr
Fg ColorAttr
Bg ColorAttr
+ ListFg ColorAttr
+ ListBg ColorAttr
SelectedFg ColorAttr
SelectedBg ColorAttr
SelectedMatch ColorAttr
@@ -323,9 +325,11 @@ type ColorTheme struct {
Scrollbar ColorAttr
Border ColorAttr
PreviewBorder ColorAttr
+ PreviewLabel ColorAttr
PreviewScrollbar ColorAttr
BorderLabel ColorAttr
- PreviewLabel ColorAttr
+ ListLabel ColorAttr
+ ListBorder ColorAttr
}
type Event struct {
@@ -395,6 +399,10 @@ func (s BorderShape) HasBottom() bool {
return true
}
+func (s BorderShape) Visible() bool {
+ return s != BorderNone
+}
+
type BorderStyle struct {
shape BorderShape
top rune
@@ -525,7 +533,16 @@ type TermSize struct {
PxHeight int
}
+type WindowType int
+
+const (
+ WindowBase WindowType = iota
+ WindowList
+ WindowPreview
+)
+
type Renderer interface {
+ DefaultTheme() *ColorTheme
Init() error
Resize(maxHeightFunc func(int) int)
Pause(clear bool)
@@ -546,7 +563,7 @@ type Renderer interface {
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 {
@@ -627,6 +644,8 @@ var (
ColPreviewLabel ColorPair
ColPreviewScrollbar ColorPair
ColPreviewSpinner ColorPair
+ ColListBorder ColorPair
+ ColListLabel ColorPair
)
func EmptyTheme() *ColorTheme {
@@ -635,6 +654,8 @@ func EmptyTheme() *ColorTheme {
Input: ColorAttr{colUndefined, AttrUndefined},
Fg: ColorAttr{colUndefined, AttrUndefined},
Bg: ColorAttr{colUndefined, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -650,6 +671,8 @@ func EmptyTheme() *ColorTheme {
Header: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
@@ -668,6 +691,8 @@ func NoColorTheme() *ColorTheme {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colDefault, AttrUndefined},
+ ListBg: ColorAttr{colDefault, AttrUndefined},
SelectedFg: ColorAttr{colDefault, AttrUndefined},
SelectedBg: ColorAttr{colDefault, AttrUndefined},
SelectedMatch: ColorAttr{colDefault, AttrUndefined},
@@ -690,6 +715,8 @@ func NoColorTheme() *ColorTheme {
PreviewBorder: ColorAttr{colDefault, AttrUndefined},
PreviewScrollbar: ColorAttr{colDefault, AttrUndefined},
PreviewLabel: ColorAttr{colDefault, AttrUndefined},
+ ListLabel: ColorAttr{colDefault, AttrUndefined},
+ ListBorder: ColorAttr{colDefault, AttrUndefined},
Separator: ColorAttr{colDefault, AttrUndefined},
Scrollbar: ColorAttr{colDefault, AttrUndefined},
}
@@ -701,6 +728,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -723,6 +752,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
}
@@ -731,6 +762,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -753,6 +786,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
}
@@ -761,6 +796,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -783,12 +820,14 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: 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 {
theme.Bg = ColorAttr{colBlack, AttrUndefined}
}
@@ -820,8 +859,10 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
// These colors are not defined in the base themes
- theme.SelectedFg = o(theme.Fg, theme.SelectedFg)
- theme.SelectedBg = o(theme.Bg, theme.SelectedBg)
+ theme.ListFg = o(theme.Fg, theme.ListFg)
+ 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.Disabled = o(theme.Input, theme.Disabled)
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.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel)
theme.PreviewBorder = o(theme.Border, theme.PreviewBorder)
- theme.Separator = o(theme.Border, theme.Separator)
- theme.Scrollbar = o(theme.Border, theme.Scrollbar)
+ theme.ListLabel = o(theme.BorderLabel, theme.ListLabel)
+ 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)
initPalette(theme)
@@ -843,19 +886,19 @@ func initPalette(theme *ColorTheme) {
}
return ColorPair{fg.Color, bg.Color, fg.Attr}
}
- blank := theme.Fg
+ blank := theme.ListFg
blank.Attr = AttrRegular
- ColPrompt = pair(theme.Prompt, theme.Bg)
- ColNormal = pair(theme.Fg, theme.Bg)
+ ColPrompt = pair(theme.Prompt, theme.ListBg)
+ ColNormal = pair(theme.ListFg, theme.ListBg)
ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
- ColInput = pair(theme.Input, theme.Bg)
- ColDisabled = pair(theme.Disabled, theme.Bg)
- ColMatch = pair(theme.Match, theme.Bg)
+ ColInput = pair(theme.Input, theme.ListBg)
+ ColDisabled = pair(theme.Disabled, theme.ListBg)
+ ColMatch = pair(theme.Match, theme.ListBg)
ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg)
ColCursor = pair(theme.Cursor, 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)
} else {
ColMarker = pair(theme.Marker, theme.Gutter)
@@ -866,11 +909,11 @@ 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.Bg)
- ColInfo = pair(theme.Info, theme.Bg)
- ColHeader = pair(theme.Header, theme.Bg)
- ColSeparator = pair(theme.Separator, theme.Bg)
- ColScrollbar = pair(theme.Scrollbar, theme.Bg)
+ ColSpinner = pair(theme.Spinner, theme.ListBg)
+ ColInfo = pair(theme.Info, theme.ListBg)
+ ColHeader = pair(theme.Header, theme.ListBg)
+ ColSeparator = pair(theme.Separator, theme.ListBg)
+ ColScrollbar = pair(theme.Scrollbar, theme.ListBg)
ColBorder = pair(theme.Border, theme.Bg)
ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
ColPreviewLabel = pair(theme.PreviewLabel, theme.PreviewBg)
@@ -878,6 +921,8 @@ func initPalette(theme *ColorTheme) {
ColPreviewBorder = pair(theme.PreviewBorder, theme.PreviewBg)
ColPreviewScrollbar = pair(theme.PreviewScrollbar, 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 {