summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/actiontype_string.go242
-rw-r--r--src/core.go2
-rw-r--r--src/options.go108
-rw-r--r--src/terminal.go232
-rw-r--r--src/tmux.go8
-rw-r--r--src/tui/light.go11
-rw-r--r--src/tui/tcell.go4
-rw-r--r--src/tui/tui.go41
-rw-r--r--src/util/util.go16
9 files changed, 489 insertions, 175 deletions
diff --git a/src/actiontype_string.go b/src/actiontype_string.go
index 8b78008f..bd141fde 100644
--- a/src/actiontype_string.go
+++ b/src/actiontype_string.go
@@ -29,128 +29,132 @@ func _() {
_ = x[actChangeBorderLabel-18]
_ = x[actChangeGhost-19]
_ = x[actChangeHeader-20]
- _ = x[actChangeHeaderLabel-21]
- _ = x[actChangeInputLabel-22]
- _ = x[actChangeListLabel-23]
- _ = x[actChangeMulti-24]
- _ = x[actChangeNth-25]
- _ = x[actChangePointer-26]
- _ = x[actChangePreview-27]
- _ = x[actChangePreviewLabel-28]
- _ = x[actChangePreviewWindow-29]
- _ = x[actChangePrompt-30]
- _ = x[actChangeQuery-31]
- _ = x[actClearScreen-32]
- _ = x[actClearQuery-33]
- _ = x[actClearSelection-34]
- _ = x[actClose-35]
- _ = x[actDeleteChar-36]
- _ = x[actDeleteCharEof-37]
- _ = x[actEndOfLine-38]
- _ = x[actFatal-39]
- _ = x[actForwardChar-40]
- _ = x[actForwardWord-41]
- _ = x[actKillLine-42]
- _ = x[actKillWord-43]
- _ = x[actUnixLineDiscard-44]
- _ = x[actUnixWordRubout-45]
- _ = x[actYank-46]
- _ = x[actBackwardKillWord-47]
- _ = x[actSelectAll-48]
- _ = x[actDeselectAll-49]
- _ = x[actToggle-50]
- _ = x[actToggleSearch-51]
- _ = x[actToggleAll-52]
- _ = x[actToggleDown-53]
- _ = x[actToggleUp-54]
- _ = x[actToggleIn-55]
- _ = x[actToggleOut-56]
- _ = x[actToggleTrack-57]
- _ = x[actToggleTrackCurrent-58]
- _ = x[actToggleHeader-59]
- _ = x[actToggleWrap-60]
- _ = x[actToggleMultiLine-61]
- _ = x[actToggleHscroll-62]
- _ = x[actTrackCurrent-63]
- _ = x[actToggleInput-64]
- _ = x[actHideInput-65]
- _ = x[actShowInput-66]
- _ = x[actUntrackCurrent-67]
- _ = x[actDown-68]
- _ = x[actUp-69]
- _ = x[actPageUp-70]
- _ = x[actPageDown-71]
- _ = x[actPosition-72]
- _ = x[actHalfPageUp-73]
- _ = x[actHalfPageDown-74]
- _ = x[actOffsetUp-75]
- _ = x[actOffsetDown-76]
- _ = x[actOffsetMiddle-77]
- _ = x[actJump-78]
- _ = x[actJumpAccept-79]
- _ = x[actPrintQuery-80]
- _ = x[actRefreshPreview-81]
- _ = x[actReplaceQuery-82]
- _ = x[actToggleSort-83]
- _ = x[actShowPreview-84]
- _ = x[actHidePreview-85]
- _ = x[actTogglePreview-86]
- _ = x[actTogglePreviewWrap-87]
- _ = x[actTransform-88]
- _ = x[actTransformBorderLabel-89]
- _ = x[actTransformGhost-90]
- _ = x[actTransformHeader-91]
- _ = x[actTransformHeaderLabel-92]
- _ = x[actTransformInputLabel-93]
- _ = x[actTransformListLabel-94]
- _ = x[actTransformNth-95]
- _ = x[actTransformPointer-96]
- _ = x[actTransformPreviewLabel-97]
- _ = x[actTransformPrompt-98]
- _ = x[actTransformQuery-99]
- _ = x[actTransformSearch-100]
- _ = x[actSearch-101]
- _ = x[actPreview-102]
- _ = x[actPreviewTop-103]
- _ = x[actPreviewBottom-104]
- _ = x[actPreviewUp-105]
- _ = x[actPreviewDown-106]
- _ = x[actPreviewPageUp-107]
- _ = x[actPreviewPageDown-108]
- _ = x[actPreviewHalfPageUp-109]
- _ = x[actPreviewHalfPageDown-110]
- _ = x[actPrevHistory-111]
- _ = x[actPrevSelected-112]
- _ = x[actPrint-113]
- _ = x[actPut-114]
- _ = x[actNextHistory-115]
- _ = x[actNextSelected-116]
- _ = x[actExecute-117]
- _ = x[actExecuteSilent-118]
- _ = x[actExecuteMulti-119]
- _ = x[actSigStop-120]
- _ = x[actFirst-121]
- _ = x[actLast-122]
- _ = x[actReload-123]
- _ = x[actReloadSync-124]
- _ = x[actDisableSearch-125]
- _ = x[actEnableSearch-126]
- _ = x[actSelect-127]
- _ = x[actDeselect-128]
- _ = x[actUnbind-129]
- _ = x[actRebind-130]
- _ = x[actToggleBind-131]
- _ = x[actBecome-132]
- _ = x[actShowHeader-133]
- _ = x[actHideHeader-134]
- _ = x[actBell-135]
- _ = x[actExclude-136]
- _ = x[actExcludeMulti-137]
+ _ = x[actChangeFooter-21]
+ _ = x[actChangeHeaderLabel-22]
+ _ = x[actChangeFooterLabel-23]
+ _ = x[actChangeInputLabel-24]
+ _ = x[actChangeListLabel-25]
+ _ = x[actChangeMulti-26]
+ _ = x[actChangeNth-27]
+ _ = x[actChangePointer-28]
+ _ = x[actChangePreview-29]
+ _ = x[actChangePreviewLabel-30]
+ _ = x[actChangePreviewWindow-31]
+ _ = x[actChangePrompt-32]
+ _ = x[actChangeQuery-33]
+ _ = x[actClearScreen-34]
+ _ = x[actClearQuery-35]
+ _ = x[actClearSelection-36]
+ _ = x[actClose-37]
+ _ = x[actDeleteChar-38]
+ _ = x[actDeleteCharEof-39]
+ _ = x[actEndOfLine-40]
+ _ = x[actFatal-41]
+ _ = x[actForwardChar-42]
+ _ = x[actForwardWord-43]
+ _ = x[actKillLine-44]
+ _ = x[actKillWord-45]
+ _ = x[actUnixLineDiscard-46]
+ _ = x[actUnixWordRubout-47]
+ _ = x[actYank-48]
+ _ = x[actBackwardKillWord-49]
+ _ = x[actSelectAll-50]
+ _ = x[actDeselectAll-51]
+ _ = x[actToggle-52]
+ _ = x[actToggleSearch-53]
+ _ = x[actToggleAll-54]
+ _ = x[actToggleDown-55]
+ _ = x[actToggleUp-56]
+ _ = x[actToggleIn-57]
+ _ = x[actToggleOut-58]
+ _ = x[actToggleTrack-59]
+ _ = x[actToggleTrackCurrent-60]
+ _ = x[actToggleHeader-61]
+ _ = x[actToggleWrap-62]
+ _ = x[actToggleMultiLine-63]
+ _ = x[actToggleHscroll-64]
+ _ = x[actTrackCurrent-65]
+ _ = x[actToggleInput-66]
+ _ = x[actHideInput-67]
+ _ = x[actShowInput-68]
+ _ = x[actUntrackCurrent-69]
+ _ = x[actDown-70]
+ _ = x[actUp-71]
+ _ = x[actPageUp-72]
+ _ = x[actPageDown-73]
+ _ = x[actPosition-74]
+ _ = x[actHalfPageUp-75]
+ _ = x[actHalfPageDown-76]
+ _ = x[actOffsetUp-77]
+ _ = x[actOffsetDown-78]
+ _ = x[actOffsetMiddle-79]
+ _ = x[actJump-80]
+ _ = x[actJumpAccept-81]
+ _ = x[actPrintQuery-82]
+ _ = x[actRefreshPreview-83]
+ _ = x[actReplaceQuery-84]
+ _ = x[actToggleSort-85]
+ _ = x[actShowPreview-86]
+ _ = x[actHidePreview-87]
+ _ = x[actTogglePreview-88]
+ _ = x[actTogglePreviewWrap-89]
+ _ = x[actTransform-90]
+ _ = x[actTransformBorderLabel-91]
+ _ = x[actTransformGhost-92]
+ _ = x[actTransformHeader-93]
+ _ = x[actTransformFooter-94]
+ _ = x[actTransformHeaderLabel-95]
+ _ = x[actTransformFooterLabel-96]
+ _ = x[actTransformInputLabel-97]
+ _ = x[actTransformListLabel-98]
+ _ = x[actTransformNth-99]
+ _ = x[actTransformPointer-100]
+ _ = x[actTransformPreviewLabel-101]
+ _ = x[actTransformPrompt-102]
+ _ = x[actTransformQuery-103]
+ _ = x[actTransformSearch-104]
+ _ = x[actSearch-105]
+ _ = x[actPreview-106]
+ _ = x[actPreviewTop-107]
+ _ = x[actPreviewBottom-108]
+ _ = x[actPreviewUp-109]
+ _ = x[actPreviewDown-110]
+ _ = x[actPreviewPageUp-111]
+ _ = x[actPreviewPageDown-112]
+ _ = x[actPreviewHalfPageUp-113]
+ _ = x[actPreviewHalfPageDown-114]
+ _ = x[actPrevHistory-115]
+ _ = x[actPrevSelected-116]
+ _ = x[actPrint-117]
+ _ = x[actPut-118]
+ _ = x[actNextHistory-119]
+ _ = x[actNextSelected-120]
+ _ = x[actExecute-121]
+ _ = x[actExecuteSilent-122]
+ _ = x[actExecuteMulti-123]
+ _ = x[actSigStop-124]
+ _ = x[actFirst-125]
+ _ = x[actLast-126]
+ _ = x[actReload-127]
+ _ = x[actReloadSync-128]
+ _ = x[actDisableSearch-129]
+ _ = x[actEnableSearch-130]
+ _ = x[actSelect-131]
+ _ = x[actDeselect-132]
+ _ = x[actUnbind-133]
+ _ = x[actRebind-134]
+ _ = x[actToggleBind-135]
+ _ = x[actBecome-136]
+ _ = x[actShowHeader-137]
+ _ = x[actHideHeader-138]
+ _ = x[actBell-139]
+ _ = x[actExclude-140]
+ _ = x[actExcludeMulti-141]
}
-const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeHeaderLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformHeaderLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti"
+const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti"
-var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 318, 337, 355, 369, 381, 397, 413, 434, 456, 471, 485, 499, 512, 529, 537, 550, 566, 578, 586, 600, 614, 625, 636, 654, 671, 678, 697, 709, 723, 732, 747, 759, 772, 783, 794, 806, 820, 841, 856, 869, 887, 903, 918, 932, 944, 956, 973, 980, 985, 994, 1005, 1016, 1029, 1044, 1055, 1068, 1083, 1090, 1103, 1116, 1133, 1148, 1161, 1175, 1189, 1205, 1225, 1237, 1260, 1277, 1295, 1318, 1340, 1361, 1376, 1395, 1419, 1437, 1454, 1472, 1481, 1491, 1504, 1520, 1532, 1546, 1562, 1580, 1600, 1622, 1636, 1651, 1659, 1665, 1679, 1694, 1704, 1720, 1735, 1745, 1753, 1760, 1769, 1782, 1798, 1813, 1822, 1833, 1842, 1851, 1864, 1873, 1886, 1899, 1906, 1916, 1931}
+var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1557, 1567, 1580, 1596, 1608, 1622, 1638, 1656, 1676, 1698, 1712, 1727, 1735, 1741, 1755, 1770, 1780, 1796, 1811, 1821, 1829, 1836, 1845, 1858, 1874, 1889, 1898, 1909, 1918, 1927, 1940, 1949, 1962, 1975, 1982, 1992, 2007}
func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) {
diff --git a/src/core.go b/src/core.go
index dffc4f36..46cd957a 100644
--- a/src/core.go
+++ b/src/core.go
@@ -39,7 +39,7 @@ func (r revision) compatible(other revision) bool {
// Run starts fzf
func Run(opts *Options) (int, error) {
if opts.Filter == nil {
- if opts.Tmux != nil && len(os.Getenv("TMUX")) > 0 && opts.Tmux.index >= opts.Height.index {
+ if opts.useTmux() {
return runTmux(os.Args, opts)
}
diff --git a/src/options.go b/src/options.go
index 142179e5..2fd6f821 100644
--- a/src/options.go
+++ b/src/options.go
@@ -83,7 +83,7 @@ Usage: fzf [options]
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--border[=STYLE] Draw border around the finder
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
- top|bottom|left|right|none] (default: rounded)
+ top|bottom|left|right|line|none] (default: rounded)
--border-label=LABEL Label to print on the border
--border-label-pos=COL Position of the border label
[POSITIVE_INTEGER: columns from left|
@@ -140,7 +140,7 @@ Usage: fzf [options]
--filepath-word Make word-wise movements respect path separators
--input-border[=STYLE] Draw border around the input section
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
- top|bottom|left|right|none] (default: rounded)
+ top|bottom|left|right|line|none] (default: rounded)
--input-label=LABEL Label to print on the input border
--input-label-pos=COL Position of the input label
[POSITIVE_INTEGER: columns from left|
@@ -168,7 +168,7 @@ Usage: fzf [options]
--header-first Print header before the prompt line
--header-border[=STYLE] Draw border around the header section
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
- top|bottom|left|right|none] (default: rounded)
+ top|bottom|left|right|line|none] (default: rounded)
--header-lines-border[=STYLE]
Display header from --header-lines with a separate border.
Pass 'none' to still separate it but without a border.
@@ -178,6 +178,17 @@ Usage: fzf [options]
NEGATIVE_INTEGER: columns from right][:bottom]
(default: 0 or center)
+ FOOTER
+ --footer=STR String to print as footer
+ --footer-border[=STYLE] Draw border around the footer section
+ [rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
+ top|bottom|left|right|line|none] (default: line)
+ --footer-label=LABEL Label to print on the footer border
+ --footer-label-pos=COL Position of the footer label
+ [POSITIVE_INTEGER: columns from left|
+ NEGATIVE_INTEGER: columns from right][:bottom]
+ (default: 0 or center)
+
SCRIPTING
-q, --query=STR Start the finder with the given query
-1, --select-1 Automatically select the only match
@@ -599,6 +610,7 @@ type Options struct {
Header []string
HeaderLines int
HeaderFirst bool
+ Footer []string
Gap int
GapLine *string
Ellipsis *string
@@ -610,8 +622,10 @@ type Options struct {
InputBorderShape tui.BorderShape
HeaderBorderShape tui.BorderShape
HeaderLinesShape tui.BorderShape
+ FooterBorderShape tui.BorderShape
InputLabel labelOpts
HeaderLabel labelOpts
+ FooterLabel labelOpts
BorderLabel labelOpts
ListLabel labelOpts
PreviewLabel labelOpts
@@ -716,6 +730,7 @@ func defaultOptions() *Options {
Header: make([]string, 0),
HeaderLines: 0,
HeaderFirst: false,
+ Footer: make([]string, 0),
Gap: 0,
Ellipsis: nil,
Scrollbar: nil,
@@ -880,12 +895,9 @@ func parseAlgo(str string) (algo.Algo, error) {
return nil, errors.New("invalid algorithm (expected: v1 or v2)")
}
-func parseBorder(str string, optional bool, allowLine bool) (tui.BorderShape, error) {
+func parseBorder(str string, optional bool) (tui.BorderShape, error) {
switch str {
case "line":
- if !allowLine {
- return tui.BorderNone, errors.New("'line' is only allowed for preview border")
- }
return tui.BorderLine, nil
case "rounded":
return tui.BorderRounded, nil
@@ -1348,6 +1360,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.HeaderBorder)
case "header-label":
mergeAttr(&theme.HeaderLabel)
+ case "footer-border":
+ mergeAttr(&theme.FooterBorder)
+ case "footer-label":
+ mergeAttr(&theme.FooterLabel)
case "spinner":
mergeAttr(&theme.Spinner)
case "info":
@@ -1360,6 +1376,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.Header)
case "header-bg":
mergeAttr(&theme.HeaderBg)
+ case "footer", "footer-fg":
+ mergeAttr(&theme.Footer)
+ case "footer-bg":
+ mergeAttr(&theme.FooterBg)
case "gap-line":
mergeAttr(&theme.GapLine)
default:
@@ -1415,7 +1435,7 @@ const (
func init() {
executeRegexp = regexp.MustCompile(
- `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header|search|nth|pointer|ghost)|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search)`)
+ `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search)`)
splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@@ -1800,6 +1820,8 @@ func isExecuteAction(str string) actionType {
return actPreview
case "change-header":
return actChangeHeader
+ case "change-footer":
+ return actChangeFooter
case "change-list-label":
return actChangeListLabel
case "change-border-label":
@@ -1810,6 +1832,8 @@ func isExecuteAction(str string) actionType {
return actChangeInputLabel
case "change-header-label":
return actChangeHeaderLabel
+ case "change-footer-label":
+ return actChangeFooterLabel
case "change-ghost":
return actChangeGhost
case "change-pointer":
@@ -1850,6 +1874,10 @@ func isExecuteAction(str string) actionType {
return actTransformInputLabel
case "transform-header-label":
return actTransformHeaderLabel
+ case "transform-footer-label":
+ return actTransformFooterLabel
+ case "transform-footer":
+ return actTransformFooter
case "transform-header":
return actTransformHeader
case "transform-ghost":
@@ -2729,6 +2757,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.HeaderLines, err = nextInt("number of header lines required"); err != nil {
return err
}
+ case "--no-footer":
+ opts.Footer = []string{}
+ case "--footer":
+ str, err := nextString("footer string required")
+ if err != nil {
+ return err
+ }
+ opts.Footer = strLines(str)
case "--header-first":
opts.HeaderFirst = true
case "--no-header-first":
@@ -2773,7 +2809,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
opts.Preview.border = tui.BorderNone
case "--preview-border":
hasArg, arg := optionalNextString()
- if opts.Preview.border, err = parseBorder(arg, !hasArg, true); err != nil {
+ if opts.Preview.border, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--height":
@@ -2812,14 +2848,17 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
opts.BorderShape = tui.BorderNone
case "--border":
hasArg, arg := optionalNextString()
- if opts.BorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
+ if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--list-border":
hasArg, arg := optionalNextString()
- if opts.ListBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
+ if opts.ListBorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
+ if opts.ListBorderShape == tui.BorderLine {
+ return errors.New("list border cannot be 'line'")
+ }
case "--no-list-border":
opts.ListBorderShape = tui.BorderNone
case "--no-list-label":
@@ -2841,14 +2880,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
opts.HeaderBorderShape = tui.BorderNone
case "--header-border":
hasArg, arg := optionalNextString()
- if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
+ if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--no-header-lines-border":
opts.HeaderLinesShape = tui.BorderNone
case "--header-lines-border":
hasArg, arg := optionalNextString()
- if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg, false); err != nil {
+ if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--no-header-label":
@@ -2865,11 +2904,32 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err := parseLabelPosition(&opts.HeaderLabel, pos); err != nil {
return err
}
+ case "--no-footer-border":
+ opts.FooterBorderShape = tui.BorderNone
+ case "--footer-border":
+ hasArg, arg := optionalNextString()
+ if opts.FooterBorderShape, err = parseBorder(arg, !hasArg); err != nil {
+ return err
+ }
+ case "--no-footer-label":
+ opts.FooterLabel.label = ""
+ case "--footer-label":
+ if opts.FooterLabel.label, err = nextString("footer label required"); err != nil {
+ return err
+ }
+ case "--footer-label-pos":
+ pos, err := nextString("footer label position required (positive or negative integer or 'center')")
+ if err != nil {
+ return err
+ }
+ if err := parseLabelPosition(&opts.FooterLabel, pos); err != nil {
+ return err
+ }
case "--no-input-border":
opts.InputBorderShape = tui.BorderNone
case "--input-border":
hasArg, arg := optionalNextString()
- if opts.InputBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
+ if opts.InputBorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--no-input-label":
@@ -3077,6 +3137,7 @@ func applyPreset(opts *Options, preset string) error {
opts.ListBorderShape = tui.BorderUndefined
opts.InputBorderShape = tui.BorderUndefined
opts.HeaderBorderShape = tui.BorderUndefined
+ opts.FooterBorderShape = tui.BorderUndefined
opts.Preview.border = defaultBorderShape
opts.Preview.info = true
opts.InfoStyle = infoDefault
@@ -3088,6 +3149,7 @@ func applyPreset(opts *Options, preset string) error {
opts.ListBorderShape = tui.BorderUndefined
opts.InputBorderShape = tui.BorderUndefined
opts.HeaderBorderShape = tui.BorderUndefined
+ opts.FooterBorderShape = tui.BorderLine
opts.Preview.border = tui.BorderLine
opts.Preview.info = false
opts.InfoStyle = infoDefault
@@ -3103,16 +3165,22 @@ func applyPreset(opts *Options, preset string) error {
}
if len(tokens) == 2 && len(tokens[1]) > 0 {
var err error
- defaultBorderShape, err = parseBorder(tokens[1], false, false)
+ defaultBorderShape, err = parseBorder(tokens[1], false)
if err != nil {
return err
}
}
- opts.ListBorderShape = defaultBorderShape
+ if defaultBorderShape != tui.BorderLine {
+ opts.ListBorderShape = defaultBorderShape
+ }
opts.InputBorderShape = defaultBorderShape
opts.HeaderBorderShape = defaultBorderShape
+ opts.FooterBorderShape = defaultBorderShape
opts.Preview.border = defaultBorderShape
+ if defaultBorderShape == tui.BorderLine {
+ opts.BorderShape = defaultBorderShape
+ }
opts.Preview.info = true
opts.InfoStyle = infoInlineRight
opts.Theme.Gutter = tui.NewColorAttr()
@@ -3185,6 +3253,10 @@ func noSeparatorLine(style infoStyle, separator bool) bool {
return false
}
+func (opts *Options) useTmux() bool {
+ return opts.Tmux != nil && len(os.Getenv("TMUX")) > 0 && opts.Tmux.index >= opts.Height.index
+}
+
func (opts *Options) noSeparatorLine() bool {
if opts.Inputless {
return true
@@ -3216,6 +3288,10 @@ func postProcessOptions(opts *Options) error {
opts.HeaderBorderShape = tui.BorderNone
}
+ if opts.FooterBorderShape == tui.BorderUndefined {
+ opts.FooterBorderShape = tui.BorderLine
+ }
+
if opts.HeaderLinesShape == tui.BorderNone {
opts.HeaderLinesShape = tui.BorderPhantom
}
diff --git a/src/terminal.go b/src/terminal.go
index a89551da..898d34c7 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -180,14 +180,18 @@ type itemLine struct {
other bool
}
+func (t *Terminal) inListWindow() bool {
+ return t.window != t.inputWindow && t.window != t.headerWindow && t.window != t.headerLinesWindow && t.window != t.footerWindow
+}
+
func (t *Terminal) markEmptyLine(line int) {
- if t.window != t.inputWindow && t.window != t.headerWindow {
+ if t.inListWindow() {
t.prevLines[line] = itemLine{valid: true, firstLine: line, empty: true}
}
}
func (t *Terminal) markOtherLine(line int) {
- if t.window != t.inputWindow && t.window != t.headerWindow {
+ if t.inListWindow() {
t.prevLines[line] = itemLine{valid: true, firstLine: line, other: true}
}
}
@@ -254,6 +258,9 @@ type Terminal struct {
headerLabel labelPrinter
headerLabelLen int
headerLabelOpts labelOpts
+ footerLabel labelPrinter
+ footerLabelLen int
+ footerLabelOpts labelOpts
pointer string
pointerLen int
pointerEmpty string
@@ -301,6 +308,7 @@ type Terminal struct {
headerLines int
header []string
header0 []string
+ footer []string
ellipsis string
scrollbar string
previewScrollbar string
@@ -322,6 +330,7 @@ type Terminal struct {
inputBorderShape tui.BorderShape
headerBorderShape tui.BorderShape
headerLinesShape tui.BorderShape
+ footerBorderShape tui.BorderShape
listLabel labelPrinter
listLabelLen int
listLabelOpts labelOpts
@@ -337,6 +346,8 @@ type Terminal struct {
headerBorder tui.Window
headerLinesWindow tui.Window
headerLinesBorder tui.Window
+ footerWindow tui.Window
+ footerBorder tui.Window
wborder tui.Window
pborder tui.Window
pwindow tui.Window
@@ -426,6 +437,7 @@ const (
reqPrompt util.EventType = iota
reqInfo
reqHeader
+ reqFooter
reqList
reqJump
reqActivate
@@ -434,6 +446,7 @@ const (
reqResize
reqRedrawInputLabel
reqRedrawHeaderLabel
+ reqRedrawFooterLabel
reqRedrawListLabel
reqRedrawBorderLabel
reqRedrawPreviewLabel
@@ -479,7 +492,9 @@ const (
actChangeBorderLabel
actChangeGhost
actChangeHeader
+ actChangeFooter
actChangeHeaderLabel
+ actChangeFooterLabel
actChangeInputLabel
actChangeListLabel
actChangeMulti
@@ -550,7 +565,9 @@ const (
actTransformBorderLabel
actTransformGhost
actTransformHeader
+ actTransformFooter
actTransformHeaderLabel
+ actTransformFooterLabel
actTransformInputLabel
actTransformListLabel
actTransformNth
@@ -907,6 +924,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
inputBorderShape: opts.InputBorderShape,
headerBorderShape: opts.HeaderBorderShape,
headerLinesShape: opts.HeaderLinesShape,
+ footerBorderShape: opts.FooterBorderShape,
borderWidth: 1,
listLabel: nil,
listLabelOpts: opts.ListLabel,
@@ -918,6 +936,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
inputLabelOpts: opts.InputLabel,
headerLabel: nil,
headerLabelOpts: opts.HeaderLabel,
+ footerLabel: nil,
+ footerLabelOpts: opts.FooterLabel,
cleanExit: opts.ClearOnExit,
executor: executor,
paused: opts.Phony,
@@ -929,6 +949,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
headerLines: opts.HeaderLines,
gap: opts.Gap,
header: []string{},
+ footer: opts.Footer,
header0: opts.Header,
ansi: opts.Ansi,
nthAttr: opts.Theme.Nth.Attr,
@@ -992,6 +1013,52 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(opts.InputLabel.label, &tui.ColInputLabel, false)
t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(opts.HeaderLabel.label, &tui.ColHeaderLabel, false)
+ t.footerLabel, t.footerLabelLen = t.ansiLabelPrinter(opts.FooterLabel.label, &tui.ColFooterLabel, false)
+
+ // Determine border shape
+ if t.borderShape == tui.BorderLine {
+ if t.fullscreen {
+ t.borderShape = tui.BorderNone
+ } else {
+ t.borderShape = tui.BorderTop
+ }
+ }
+
+ // Determine input border shape
+ if t.inputBorderShape == tui.BorderLine {
+ if t.layout == layoutReverse {
+ t.inputBorderShape = tui.BorderBottom
+ } else {
+ t.inputBorderShape = tui.BorderTop
+ }
+ }
+
+ // Determine header border shape
+ if t.headerBorderShape == tui.BorderLine {
+ if t.layout == layoutReverse {
+ t.headerBorderShape = tui.BorderBottom
+ } else {
+ t.headerBorderShape = tui.BorderTop
+ }
+ }
+
+ // Determine header lines border shape
+ if t.headerLinesShape == tui.BorderLine {
+ if t.layout == layoutDefault {
+ t.headerLinesShape = tui.BorderTop
+ } else {
+ t.headerLinesShape = tui.BorderBottom
+ }
+ }
+
+ // Determine footer border shape
+ if t.footerBorderShape == tui.BorderLine {
+ if t.layout == layoutReverse {
+ t.footerBorderShape = tui.BorderTop
+ } else {
+ t.footerBorderShape = tui.BorderBottom
+ }
+ }
// Disable separator by default if input border is set
if opts.Separator == nil && !t.inputBorderShape.Visible() || opts.Separator != nil && len(*opts.Separator) > 0 {
@@ -1208,6 +1275,10 @@ func (t *Terminal) extraLines() int {
}
extra += t.headerLines
}
+ if len(t.footer) > 0 {
+ extra += borderLines(t.footerBorderShape)
+ extra += len(t.footer)
+ }
return extra
}
@@ -1475,6 +1546,16 @@ func (t *Terminal) changeHeader(header string) bool {
return needFullRedraw
}
+func (t *Terminal) changeFooter(footer string) bool {
+ var lines []string
+ if len(footer) > 0 {
+ lines = strings.Split(strings.TrimSuffix(footer, "\n"), "\n")
+ }
+ needFullRedraw := len(t.footer) != len(lines)
+ t.footer = lines
+ return needFullRedraw
+}
+
// UpdateHeader updates the header
func (t *Terminal) UpdateHeader(header []string) {
t.mutex.Lock()
@@ -1835,6 +1916,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if t.headerBorder != nil {
t.headerBorder = nil
}
+ if t.footerWindow != nil {
+ t.footerWindow = nil
+ }
+ if t.footerBorder != nil {
+ t.footerBorder = nil
+ }
if t.headerLinesWindow != nil {
t.headerLinesWindow = nil
}
@@ -1889,17 +1976,19 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
// Adjust position and size of the list window if input border is set
inputBorderHeight := 0
availableLines := height
+
shift := 0
shrink := 0
hasHeaderWindow := t.hasHeaderWindow()
+ hasFooterWindow := len(t.footer) > 0
hasHeaderLinesWindow, headerLinesShape := t.determineHeaderLinesShape()
hasInputWindow := !t.inputless && (t.inputBorderShape.Visible() || hasHeaderWindow || hasHeaderLinesWindow)
+ inputWindowHeight := 2
+ if t.noSeparatorLine() {
+ inputWindowHeight--
+ }
if hasInputWindow {
- inputWindowHeight := 2
- if t.noSeparatorLine() {
- inputWindowHeight--
- }
- inputBorderHeight = util.Min(availableLines, borderLines(t.inputBorderShape)+inputWindowHeight)
+ inputBorderHeight = util.Constrain(borderLines(t.inputBorderShape)+inputWindowHeight, 0, availableLines)
if t.layout == layoutReverse {
shift = inputBorderHeight
shrink = inputBorderHeight
@@ -1907,6 +1996,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
shrink = inputBorderHeight
}
availableLines -= inputBorderHeight
+ } else if !t.inputless {
+ availableLines -= inputWindowHeight
+ }
+
+ // FIXME: Needed?
+ if t.needPreviewWindow() {
+ _, minPreviewHeight := t.minPreviewSize(t.activePreviewOpts)
+ switch t.activePreviewOpts.position {
+ case posUp, posDown:
+ availableLines -= minPreviewHeight
+ }
}
// Adjust position and size of the list window if header border is set
@@ -1916,7 +2016,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if hasHeaderLinesWindow {
headerWindowHeight -= t.headerLines
}
- headerBorderHeight = util.Min(availableLines, borderLines(t.headerBorderShape)+headerWindowHeight)
+ headerBorderHeight = util.Constrain(borderLines(t.headerBorderShape)+headerWindowHeight, 0, availableLines)
if t.layout == layoutReverse {
shift += headerBorderHeight
shrink += headerBorderHeight
@@ -1928,7 +2028,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
headerLinesHeight := 0
if hasHeaderLinesWindow {
- headerLinesHeight = util.Min(availableLines, borderLines(headerLinesShape)+t.headerLines)
+ headerLinesHeight = util.Constrain(borderLines(headerLinesShape)+t.headerLines, 0, availableLines)
if t.layout != layoutDefault {
shift += headerLinesHeight
shrink += headerLinesHeight
@@ -1938,6 +2038,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
availableLines -= headerLinesHeight
}
+ footerBorderHeight := 0
+ if hasFooterWindow {
+ // Footer lines should not take all available lines
+ footerBorderHeight = util.Constrain(borderLines(t.footerBorderShape)+len(t.footer), 0, availableLines)
+ shrink += footerBorderHeight
+ if t.layout != layoutReverse {
+ shift += footerBorderHeight
+ }
+ availableLines -= footerBorderHeight
+ }
+
// Set up list border
hasListBorder := t.listBorderShape.Visible()
innerWidth := width
@@ -2041,13 +2152,8 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
width++
}
- maxPreviewLines := availableLines
- if t.wborder != nil {
- maxPreviewLines -= t.wborder.Height()
- } else {
- maxPreviewLines -= util.Max(0, innerHeight-pheight-shrink)
- }
- pheight = util.Min(pheight, maxPreviewLines)
+ pheight = util.Constrain(pheight, minPreviewHeight, availableLines)
+
if previewOpts.position == posUp {
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow(
@@ -2210,7 +2316,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
case layoutDefault:
btop = w.Top() + w.Height() + headerBorderHeight + headerLinesHeight
case layoutReverse:
- btop = w.Top() - shrink
+ btop = w.Top() - shrink + footerBorderHeight
case layoutReverseList:
btop = w.Top() + w.Height() + headerBorderHeight
}
@@ -2238,7 +2344,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
var btop int
if hasInputWindow && t.headerFirst {
if t.layout == layoutReverse {
- btop = w.Top() - shrink
+ btop = w.Top() - shrink + footerBorderHeight
} else if t.layout == layoutReverseList {
btop = w.Top() + w.Height() + inputBorderHeight
} else {
@@ -2294,12 +2400,31 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
t.headerLinesWindow = createInnerWindow(t.headerLinesBorder, headerLinesShape, tui.WindowHeader, 0)
}
+ // Set up footer
+ if hasFooterWindow {
+ var btop int
+ if t.layout == layoutReverse {
+ btop = w.Top() + w.Height()
+ } else if t.layout == layoutReverseList {
+ btop = w.Top() - footerBorderHeight - headerLinesHeight
+ } else {
+ btop = w.Top() - footerBorderHeight
+ }
+ t.footerBorder = t.tui.NewWindow(
+ btop,
+ w.Left(),
+ w.Width(),
+ footerBorderHeight, tui.WindowFooter, tui.MakeBorderStyle(t.footerBorderShape, t.unicode), true)
+ t.footerWindow = createInnerWindow(t.footerBorder, t.footerBorderShape, tui.WindowFooter, 0)
+ }
+
// Print border label
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, false)
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.Border(), false)
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, false)
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, false)
+ t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, false)
}
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
@@ -2343,7 +2468,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
case layoutDefault:
y = h - y - 1
case layoutReverseList:
- if t.window == t.inputWindow || t.window == t.headerWindow {
+ if !t.inListWindow() && t.window != t.headerLinesWindow {
// From bottom to top
y = h - y - 1
} else {
@@ -2690,8 +2815,10 @@ func (t *Terminal) resizeIfNeeded() bool {
if t.hasHeaderLinesWindow() {
primaryHeaderLines -= t.headerLines
}
+ // FIXME: Full redraw is triggered if there are too many lines in the header
+ // so that the header window cannot display all of them.
needHeaderLinesWindow := t.hasHeaderLinesWindow()
- if (t.headerBorderShape.Visible() || t.hasHeaderLinesWindow()) &&
+ if (t.headerBorderShape.Visible() || needHeaderLinesWindow) &&
(t.headerWindow == nil && primaryHeaderLines > 0 || t.headerWindow != nil && primaryHeaderLines != t.headerWindow.Height()) ||
needHeaderLinesWindow && (t.headerLinesWindow == nil || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) ||
!needHeaderLinesWindow && t.headerLinesWindow != nil {
@@ -2720,6 +2847,41 @@ func (t *Terminal) printHeader() {
}
}
+func (t *Terminal) printFooter() {
+ if len(t.footer) == 0 {
+ return
+ }
+ indentSize := t.headerIndent(t.footerBorderShape)
+ indent := strings.Repeat(" ", indentSize)
+ max := util.Min(len(t.footer), t.footerWindow.Height())
+
+ // Wrapping is not supported for footer
+ wrap := t.wrap
+ t.wrap = false
+ t.withWindow(t.footerWindow, func() {
+ var state *ansiState
+ for idx, lineStr := range t.footer[:max] {
+ line := idx
+ if t.layout != layoutReverse {
+ line = max - idx - 1
+ }
+ trimmed, colors, newState := extractColor(lineStr, state, nil)
+ state = newState
+ item := &Item{
+ text: util.ToChars([]byte(trimmed)),
+ colors: colors}
+
+ t.printHighlighted(Result{item: item},
+ tui.ColFooter, tui.ColFooter, false, false, line, line, true,
+ func(markerClass) int {
+ t.footerWindow.Print(indent)
+ return indentSize
+ }, nil)
+ }
+ })
+ t.wrap = wrap
+}
+
func (t *Terminal) headerIndent(borderShape tui.BorderShape) int {
indentSize := t.pointerLen + t.markerLen
if t.listBorderShape.HasLeft() {
@@ -2792,7 +2954,7 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
}
func (t *Terminal) canSpanMultiLines() bool {
- return t.multiLine || t.wrap || t.gap > 0
+ return (t.multiLine || t.wrap || t.gap > 0) && t.inListWindow()
}
func (t *Terminal) renderBar(line int, barRange [2]int) {
@@ -3767,6 +3929,7 @@ func (t *Terminal) printAll() {
t.printPrompt()
t.printInfo()
t.printHeader()
+ t.printFooter()
t.printPreview()
}
@@ -4515,6 +4678,7 @@ func (t *Terminal) Loop() error {
t.reqBox.Set(reqPrompt, nil)
t.reqBox.Set(reqInfo, nil)
t.reqBox.Set(reqHeader, nil)
+ t.reqBox.Set(reqFooter, nil)
if t.initDelay > 0 {
go func() {
timer := time.NewTimer(t.initDelay)
@@ -4797,6 +4961,10 @@ func (t *Terminal) Loop() error {
if !t.resizeIfNeeded() {
t.printHeader()
}
+ case reqFooter:
+ if !t.resizeIfNeeded() {
+ t.printFooter()
+ }
case reqActivate:
t.suppress = false
if t.hasPreviewer() {
@@ -4806,6 +4974,8 @@ func (t *Terminal) Loop() error {
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
case reqRedrawHeaderLabel:
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, true)
+ case reqRedrawFooterLabel:
+ t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, true)
case reqRedrawListLabel:
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
case reqRedrawBorderLabel:
@@ -4996,7 +5166,7 @@ func (t *Terminal) Loop() error {
}
updatePreviewWindow := func(forcePreview bool) {
t.resizeWindows(forcePreview, false)
- req(reqPrompt, reqList, reqInfo, reqHeader)
+ req(reqPrompt, reqList, reqInfo, reqHeader, reqFooter)
}
toggle := func() bool {
current := t.currentItem()
@@ -5271,6 +5441,16 @@ func (t *Terminal) Loop() error {
} else {
req(reqHeader)
}
+ case actChangeFooter, actTransformFooter:
+ footer := a.a
+ if a.t == actTransformFooter {
+ footer = t.captureLines(a.a)
+ }
+ if t.changeFooter(footer) {
+ req(reqFullRedraw)
+ } else {
+ req(reqFooter)
+ }
case actChangeHeaderLabel, actTransformHeaderLabel:
label := a.a
if a.t == actTransformHeaderLabel {
@@ -5279,6 +5459,14 @@ func (t *Terminal) Loop() error {
t.headerLabelOpts.label = label
t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false)
req(reqRedrawHeaderLabel)
+ case actChangeFooterLabel, actTransformFooterLabel:
+ label := a.a
+ if a.t == actTransformFooterLabel {
+ label = t.captureLine(a.a)
+ }
+ t.footerLabelOpts.label = label
+ t.footerLabel, t.footerLabelLen = t.ansiLabelPrinter(label, &tui.ColFooterLabel, false)
+ req(reqRedrawFooterLabel)
case actChangeInputLabel, actTransformInputLabel:
label := a.a
if a.t == actTransformInputLabel {
diff --git a/src/tmux.go b/src/tmux.go
index 87ea38cc..2e8499c5 100644
--- a/src/tmux.go
+++ b/src/tmux.go
@@ -11,10 +11,14 @@ func runTmux(args []string, opts *Options) (int, error) {
// Prepare arguments
fzf, rest := args[0], args[1:]
args = []string{"--bind=ctrl-z:ignore"}
- if !opts.Tmux.border && opts.BorderShape == tui.BorderUndefined {
+ if !opts.Tmux.border && (opts.BorderShape == tui.BorderUndefined || opts.BorderShape == tui.BorderLine) {
// We append --border option at the end, because `--style=full:STYLE`
// may have changed the default border style.
- rest = append(rest, "--border")
+ if tui.DefaultBorderShape == tui.BorderRounded {
+ rest = append(rest, "--border=rounded")
+ } else {
+ rest = append(rest, "--border=sharp")
+ }
}
if opts.Tmux.border && opts.Margin == defaultMargin() {
args = append(args, "--margin=0,1")
diff --git a/src/tui/light.go b/src/tui/light.go
index eb3de098..54abbe5c 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -829,11 +829,14 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, wind
case WindowHeader:
w.fg = r.theme.Header.Color
w.bg = r.theme.HeaderBg.Color
+ case WindowFooter:
+ w.fg = r.theme.Footer.Color
+ w.bg = r.theme.FooterBg.Color
case WindowPreview:
w.fg = r.theme.PreviewFg.Color
w.bg = r.theme.PreviewBg.Color
}
- if erase && !w.bg.IsDefault() && w.border.shape != BorderNone {
+ if erase && !w.bg.IsDefault() && w.border.shape != BorderNone && w.height > 0 {
// fzf --color bg:blue --border --padding 1,2
w.Erase()
}
@@ -889,6 +892,8 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
color = ColInputBorder
case WindowHeader:
color = ColHeaderBorder
+ case WindowFooter:
+ color = ColFooterBorder
case WindowPreview:
color = ColPreviewBorder
}
@@ -914,6 +919,8 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
color = ColInputBorder
case WindowHeader:
color = ColHeaderBorder
+ case WindowFooter:
+ color = ColFooterBorder
case WindowPreview:
color = ColPreviewBorder
}
@@ -941,6 +948,8 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
color = ColInputBorder
case WindowHeader:
color = ColHeaderBorder
+ case WindowFooter:
+ color = ColFooterBorder
case WindowPreview:
color = ColPreviewBorder
}
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index a2630463..762768da 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -600,6 +600,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
normal = ColNormal
case WindowHeader:
normal = ColHeader
+ case WindowFooter:
+ normal = ColFooter
case WindowInput:
normal = ColInput
case WindowPreview:
@@ -865,6 +867,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
style = ColListBorder.style()
case WindowHeader:
style = ColHeaderBorder.style()
+ case WindowFooter:
+ style = ColFooterBorder.style()
case WindowInput:
style = ColInputBorder.style()
case WindowPreview:
diff --git a/src/tui/tui.go b/src/tui/tui.go
index 06dbccb0..c8844753 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -359,6 +359,10 @@ type ColorTheme struct {
HeaderBg ColorAttr
HeaderBorder ColorAttr
HeaderLabel ColorAttr
+ Footer ColorAttr
+ FooterBg ColorAttr
+ FooterBorder ColorAttr
+ FooterLabel ColorAttr
Separator ColorAttr
Scrollbar ColorAttr
Border ColorAttr
@@ -612,6 +616,7 @@ const (
WindowPreview
WindowInput
WindowHeader
+ WindowFooter
)
type Renderer interface {
@@ -720,6 +725,9 @@ var (
ColHeader ColorPair
ColHeaderBorder ColorPair
ColHeaderLabel ColorPair
+ ColFooter ColorPair
+ ColFooterBorder ColorPair
+ ColFooterLabel ColorPair
ColSeparator ColorPair
ColScrollbar ColorPair
ColGapLine ColorPair
@@ -758,6 +766,7 @@ func EmptyTheme() *ColorTheme {
Cursor: ColorAttr{colUndefined, AttrUndefined},
Marker: ColorAttr{colUndefined, AttrUndefined},
Header: ColorAttr{colUndefined, AttrUndefined},
+ Footer: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: ColorAttr{colUndefined, AttrUndefined},
@@ -778,6 +787,9 @@ func EmptyTheme() *ColorTheme {
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
+ FooterBg: ColorAttr{colUndefined, AttrUndefined},
+ FooterBorder: ColorAttr{colUndefined, AttrUndefined},
+ FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
@@ -825,6 +837,9 @@ func NoColorTheme() *ColorTheme {
HeaderBg: ColorAttr{colDefault, AttrUndefined},
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
+ FooterBg: ColorAttr{colDefault, AttrUndefined},
+ FooterBorder: ColorAttr{colDefault, AttrUndefined},
+ FooterLabel: ColorAttr{colDefault, AttrUndefined},
GapLine: ColorAttr{colDefault, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
@@ -852,6 +867,7 @@ func init() {
Cursor: ColorAttr{colRed, AttrUndefined},
Marker: ColorAttr{colMagenta, AttrUndefined},
Header: ColorAttr{colCyan, AttrUndefined},
+ Footer: ColorAttr{colCyan, AttrUndefined},
Border: ColorAttr{colBlack, AttrUndefined},
BorderLabel: ColorAttr{colWhite, AttrUndefined},
Ghost: ColorAttr{colUndefined, Dim},
@@ -869,6 +885,12 @@ func init() {
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBg: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
+ HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
+ FooterBg: ColorAttr{colUndefined, AttrUndefined},
+ FooterBorder: ColorAttr{colUndefined, AttrUndefined},
+ FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
@@ -893,6 +915,7 @@ func init() {
Cursor: ColorAttr{161, AttrUndefined},
Marker: ColorAttr{168, AttrUndefined},
Header: ColorAttr{109, AttrUndefined},
+ Footer: ColorAttr{109, AttrUndefined},
Border: ColorAttr{59, AttrUndefined},
BorderLabel: ColorAttr{145, AttrUndefined},
Ghost: ColorAttr{colUndefined, Dim},
@@ -910,6 +933,12 @@ func init() {
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBg: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
+ HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
+ FooterBg: ColorAttr{colUndefined, AttrUndefined},
+ FooterBorder: ColorAttr{colUndefined, AttrUndefined},
+ FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
@@ -934,6 +963,7 @@ func init() {
Cursor: ColorAttr{161, AttrUndefined},
Marker: ColorAttr{168, AttrUndefined},
Header: ColorAttr{31, AttrUndefined},
+ Footer: ColorAttr{31, AttrUndefined},
Border: ColorAttr{145, AttrUndefined},
BorderLabel: ColorAttr{59, AttrUndefined},
Ghost: ColorAttr{colUndefined, Dim},
@@ -954,6 +984,9 @@ func init() {
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
+ FooterBg: ColorAttr{colUndefined, AttrUndefined},
+ FooterBorder: ColorAttr{colUndefined, AttrUndefined},
+ FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
@@ -989,6 +1022,7 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
theme.Marker = o(baseTheme.Marker, theme.Marker)
theme.Header = o(baseTheme.Header, theme.Header)
+ theme.Footer = o(baseTheme.Footer, theme.Footer)
theme.Border = o(baseTheme.Border, theme.Border)
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
@@ -1042,6 +1076,10 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp
theme.HeaderBorder = o(theme.Border, theme.HeaderBorder)
theme.HeaderLabel = o(theme.BorderLabel, theme.HeaderLabel)
+ theme.FooterBg = o(theme.Bg, theme.FooterBg)
+ theme.FooterBorder = o(theme.Border, theme.FooterBorder)
+ theme.FooterLabel = o(theme.BorderLabel, theme.FooterLabel)
+
initPalette(theme)
}
@@ -1095,6 +1133,9 @@ func initPalette(theme *ColorTheme) {
ColHeader = pair(theme.Header, theme.HeaderBg)
ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
ColHeaderLabel = pair(theme.HeaderLabel, theme.HeaderBg)
+ ColFooter = pair(theme.Footer, theme.FooterBg)
+ ColFooterBorder = pair(theme.FooterBorder, theme.FooterBg)
+ ColFooterLabel = pair(theme.FooterLabel, theme.FooterBg)
}
func runeWidth(r rune) int {
diff --git a/src/util/util.go b/src/util/util.go
index c8301363..f11a0887 100644
--- a/src/util/util.go
+++ b/src/util/util.go
@@ -97,24 +97,12 @@ func Min32(first int32, second int32) int32 {
// Constrain32 limits the given 32-bit integer with the upper and lower bounds
func Constrain32(val int32, min int32, max int32) int32 {
- if val < min {
- return min
- }
- if val > max {
- return max
- }
- return val
+ return Max32(Min32(val, max), min)
}
// Constrain limits the given integer with the upper and lower bounds
func Constrain(val int, min int, max int) int {
- if val < min {
- return min
- }
- if val > max {
- return max
- }
- return val
+ return Max(Min(val, max), min)
}
func AsUint16(val int) uint16 {