mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-21 07:23:49 -07:00
Add footer
Options: --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) The default border type for footer is 'line', which draws a single separator between the footer and the list. It changes its position depending on `--layout`, so you don't have to manually switch between 'top' and 'bottom' The 'line' style is now supported by other border types as well. `--list-border` is the only exception.
This commit is contained in:
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
..
|
..
|
||||||
.TH fzf 1 "May 2025" "fzf 0.62.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Jun 2025" "fzf 0.63.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -503,6 +503,8 @@ Draw border around the finder
|
|||||||
.br
|
.br
|
||||||
.BR vertical " Vertical lines on each side of the finder"
|
.BR vertical " Vertical lines on each side of the finder"
|
||||||
.br
|
.br
|
||||||
|
.BR line " Single line border (position automatically determined)"
|
||||||
|
.br
|
||||||
.BR top " (up)"
|
.BR top " (up)"
|
||||||
.br
|
.br
|
||||||
.BR bottom " (down)"
|
.BR bottom " (down)"
|
||||||
@@ -518,6 +520,9 @@ If you use a terminal emulator where each box-drawing character takes
|
|||||||
2 columns, try setting \fB\-\-ambidouble\fR. If the border is still not properly
|
2 columns, try setting \fB\-\-ambidouble\fR. If the border is still not properly
|
||||||
rendered, set \fB\-\-no\-unicode\fR.
|
rendered, set \fB\-\-no\-unicode\fR.
|
||||||
|
|
||||||
|
\fBline\fR style draws a single separator line at the top when \fB\-\-height\fR
|
||||||
|
is used.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-border\-label" [=LABEL]
|
.BI "\-\-border\-label" [=LABEL]
|
||||||
Label to print on the horizontal border line. Should be used with one of the
|
Label to print on the horizontal border line. Should be used with one of the
|
||||||
@@ -661,7 +666,8 @@ Do not display scrollbar. A synonym for \fB\-\-scrollbar=''\fB
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-list\-border" [=STYLE]
|
.BI "\-\-list\-border" [=STYLE]
|
||||||
Draw border around the list section
|
Draw border around the list section. \fBline\fR style is not supported for
|
||||||
|
this border.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-list\-label" [=LABEL]
|
.BI "\-\-list\-label" [=LABEL]
|
||||||
@@ -748,7 +754,8 @@ actions are affected:
|
|||||||
\fBkill\-word\fR
|
\fBkill\-word\fR
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-input\-border" [=STYLE]
|
.BI "\-\-input\-border" [=STYLE]
|
||||||
Draw border around the input section
|
Draw border around the input section. \fBline\fR style draws a single separator
|
||||||
|
line between the input section and the list section.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-input\-label" [=LABEL]
|
.BI "\-\-input\-label" [=LABEL]
|
||||||
@@ -848,8 +855,7 @@ e.g.
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-preview\-border" [=STYLE]
|
.BI "\-\-preview\-border" [=STYLE]
|
||||||
Short for \fB\-\-preview\-window=border\-STYLE\fR. In addition to the other
|
Short for \fB\-\-preview\-window=border\-STYLE\fR. \fBline\fR style draws
|
||||||
styles, \fBline\fR style is also supported for preview border, which draws
|
|
||||||
a single separator line between the preview window and the rest of the
|
a single separator line between the preview window and the rest of the
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
@@ -1005,7 +1011,8 @@ Print header before the prompt line. When both normal header and header lines
|
|||||||
(\fB\-\-header\-lines\fR) are present, this applies only to the normal header.
|
(\fB\-\-header\-lines\fR) are present, this applies only to the normal header.
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-border" [=STYLE]
|
.BI "\-\-header\-border" [=STYLE]
|
||||||
Draw border around the header section
|
Draw border around the header section. \fBline\fR style draws a single
|
||||||
|
separator line between the header window and the list section.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-label" [=LABEL]
|
.BI "\-\-header\-label" [=LABEL]
|
||||||
@@ -1019,7 +1026,30 @@ Position of the header label
|
|||||||
.BI "\-\-header\-lines\-border" [=STYLE]
|
.BI "\-\-header\-lines\-border" [=STYLE]
|
||||||
Display header from \fB--header\-lines\fR with a separate border. Pass
|
Display header from \fB--header\-lines\fR with a separate border. Pass
|
||||||
\fBnone\fR to still separate the header lines but without a border. To combine
|
\fBnone\fR to still separate the header lines but without a border. To combine
|
||||||
two headers, use \fB\-\-no\-header\-lines\-border\fR.
|
two headers, use \fB\-\-no\-header\-lines\-border\fR. \fBline\fR style draws
|
||||||
|
a single separator line between the header lines and the list section.
|
||||||
|
|
||||||
|
.SS FOOTER
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer=" "STR"
|
||||||
|
The given string will be printed as the sticky footer. The lines are displayed
|
||||||
|
in the given order from top to bottom regardless of \fB\-\-layout\fR option, and
|
||||||
|
are not affected by \fB\-\-with\-nth\fR. ANSI color codes are processed even when
|
||||||
|
\fB\-\-ansi\fR is not set.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-border" [=STYLE]
|
||||||
|
Draw border around the header section. \fBline\fR style draws a single
|
||||||
|
separator line between the footer and the list section.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-label" [=LABEL]
|
||||||
|
Label to print on the footer border
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-label\-pos" [=N[:top|bottom]]
|
||||||
|
Position of the footer label
|
||||||
|
|
||||||
.SS SCRIPTING
|
.SS SCRIPTING
|
||||||
.TP
|
.TP
|
||||||
|
@@ -29,128 +29,132 @@ func _() {
|
|||||||
_ = x[actChangeBorderLabel-18]
|
_ = x[actChangeBorderLabel-18]
|
||||||
_ = x[actChangeGhost-19]
|
_ = x[actChangeGhost-19]
|
||||||
_ = x[actChangeHeader-20]
|
_ = x[actChangeHeader-20]
|
||||||
_ = x[actChangeHeaderLabel-21]
|
_ = x[actChangeFooter-21]
|
||||||
_ = x[actChangeInputLabel-22]
|
_ = x[actChangeHeaderLabel-22]
|
||||||
_ = x[actChangeListLabel-23]
|
_ = x[actChangeFooterLabel-23]
|
||||||
_ = x[actChangeMulti-24]
|
_ = x[actChangeInputLabel-24]
|
||||||
_ = x[actChangeNth-25]
|
_ = x[actChangeListLabel-25]
|
||||||
_ = x[actChangePointer-26]
|
_ = x[actChangeMulti-26]
|
||||||
_ = x[actChangePreview-27]
|
_ = x[actChangeNth-27]
|
||||||
_ = x[actChangePreviewLabel-28]
|
_ = x[actChangePointer-28]
|
||||||
_ = x[actChangePreviewWindow-29]
|
_ = x[actChangePreview-29]
|
||||||
_ = x[actChangePrompt-30]
|
_ = x[actChangePreviewLabel-30]
|
||||||
_ = x[actChangeQuery-31]
|
_ = x[actChangePreviewWindow-31]
|
||||||
_ = x[actClearScreen-32]
|
_ = x[actChangePrompt-32]
|
||||||
_ = x[actClearQuery-33]
|
_ = x[actChangeQuery-33]
|
||||||
_ = x[actClearSelection-34]
|
_ = x[actClearScreen-34]
|
||||||
_ = x[actClose-35]
|
_ = x[actClearQuery-35]
|
||||||
_ = x[actDeleteChar-36]
|
_ = x[actClearSelection-36]
|
||||||
_ = x[actDeleteCharEof-37]
|
_ = x[actClose-37]
|
||||||
_ = x[actEndOfLine-38]
|
_ = x[actDeleteChar-38]
|
||||||
_ = x[actFatal-39]
|
_ = x[actDeleteCharEof-39]
|
||||||
_ = x[actForwardChar-40]
|
_ = x[actEndOfLine-40]
|
||||||
_ = x[actForwardWord-41]
|
_ = x[actFatal-41]
|
||||||
_ = x[actKillLine-42]
|
_ = x[actForwardChar-42]
|
||||||
_ = x[actKillWord-43]
|
_ = x[actForwardWord-43]
|
||||||
_ = x[actUnixLineDiscard-44]
|
_ = x[actKillLine-44]
|
||||||
_ = x[actUnixWordRubout-45]
|
_ = x[actKillWord-45]
|
||||||
_ = x[actYank-46]
|
_ = x[actUnixLineDiscard-46]
|
||||||
_ = x[actBackwardKillWord-47]
|
_ = x[actUnixWordRubout-47]
|
||||||
_ = x[actSelectAll-48]
|
_ = x[actYank-48]
|
||||||
_ = x[actDeselectAll-49]
|
_ = x[actBackwardKillWord-49]
|
||||||
_ = x[actToggle-50]
|
_ = x[actSelectAll-50]
|
||||||
_ = x[actToggleSearch-51]
|
_ = x[actDeselectAll-51]
|
||||||
_ = x[actToggleAll-52]
|
_ = x[actToggle-52]
|
||||||
_ = x[actToggleDown-53]
|
_ = x[actToggleSearch-53]
|
||||||
_ = x[actToggleUp-54]
|
_ = x[actToggleAll-54]
|
||||||
_ = x[actToggleIn-55]
|
_ = x[actToggleDown-55]
|
||||||
_ = x[actToggleOut-56]
|
_ = x[actToggleUp-56]
|
||||||
_ = x[actToggleTrack-57]
|
_ = x[actToggleIn-57]
|
||||||
_ = x[actToggleTrackCurrent-58]
|
_ = x[actToggleOut-58]
|
||||||
_ = x[actToggleHeader-59]
|
_ = x[actToggleTrack-59]
|
||||||
_ = x[actToggleWrap-60]
|
_ = x[actToggleTrackCurrent-60]
|
||||||
_ = x[actToggleMultiLine-61]
|
_ = x[actToggleHeader-61]
|
||||||
_ = x[actToggleHscroll-62]
|
_ = x[actToggleWrap-62]
|
||||||
_ = x[actTrackCurrent-63]
|
_ = x[actToggleMultiLine-63]
|
||||||
_ = x[actToggleInput-64]
|
_ = x[actToggleHscroll-64]
|
||||||
_ = x[actHideInput-65]
|
_ = x[actTrackCurrent-65]
|
||||||
_ = x[actShowInput-66]
|
_ = x[actToggleInput-66]
|
||||||
_ = x[actUntrackCurrent-67]
|
_ = x[actHideInput-67]
|
||||||
_ = x[actDown-68]
|
_ = x[actShowInput-68]
|
||||||
_ = x[actUp-69]
|
_ = x[actUntrackCurrent-69]
|
||||||
_ = x[actPageUp-70]
|
_ = x[actDown-70]
|
||||||
_ = x[actPageDown-71]
|
_ = x[actUp-71]
|
||||||
_ = x[actPosition-72]
|
_ = x[actPageUp-72]
|
||||||
_ = x[actHalfPageUp-73]
|
_ = x[actPageDown-73]
|
||||||
_ = x[actHalfPageDown-74]
|
_ = x[actPosition-74]
|
||||||
_ = x[actOffsetUp-75]
|
_ = x[actHalfPageUp-75]
|
||||||
_ = x[actOffsetDown-76]
|
_ = x[actHalfPageDown-76]
|
||||||
_ = x[actOffsetMiddle-77]
|
_ = x[actOffsetUp-77]
|
||||||
_ = x[actJump-78]
|
_ = x[actOffsetDown-78]
|
||||||
_ = x[actJumpAccept-79]
|
_ = x[actOffsetMiddle-79]
|
||||||
_ = x[actPrintQuery-80]
|
_ = x[actJump-80]
|
||||||
_ = x[actRefreshPreview-81]
|
_ = x[actJumpAccept-81]
|
||||||
_ = x[actReplaceQuery-82]
|
_ = x[actPrintQuery-82]
|
||||||
_ = x[actToggleSort-83]
|
_ = x[actRefreshPreview-83]
|
||||||
_ = x[actShowPreview-84]
|
_ = x[actReplaceQuery-84]
|
||||||
_ = x[actHidePreview-85]
|
_ = x[actToggleSort-85]
|
||||||
_ = x[actTogglePreview-86]
|
_ = x[actShowPreview-86]
|
||||||
_ = x[actTogglePreviewWrap-87]
|
_ = x[actHidePreview-87]
|
||||||
_ = x[actTransform-88]
|
_ = x[actTogglePreview-88]
|
||||||
_ = x[actTransformBorderLabel-89]
|
_ = x[actTogglePreviewWrap-89]
|
||||||
_ = x[actTransformGhost-90]
|
_ = x[actTransform-90]
|
||||||
_ = x[actTransformHeader-91]
|
_ = x[actTransformBorderLabel-91]
|
||||||
_ = x[actTransformHeaderLabel-92]
|
_ = x[actTransformGhost-92]
|
||||||
_ = x[actTransformInputLabel-93]
|
_ = x[actTransformHeader-93]
|
||||||
_ = x[actTransformListLabel-94]
|
_ = x[actTransformFooter-94]
|
||||||
_ = x[actTransformNth-95]
|
_ = x[actTransformHeaderLabel-95]
|
||||||
_ = x[actTransformPointer-96]
|
_ = x[actTransformFooterLabel-96]
|
||||||
_ = x[actTransformPreviewLabel-97]
|
_ = x[actTransformInputLabel-97]
|
||||||
_ = x[actTransformPrompt-98]
|
_ = x[actTransformListLabel-98]
|
||||||
_ = x[actTransformQuery-99]
|
_ = x[actTransformNth-99]
|
||||||
_ = x[actTransformSearch-100]
|
_ = x[actTransformPointer-100]
|
||||||
_ = x[actSearch-101]
|
_ = x[actTransformPreviewLabel-101]
|
||||||
_ = x[actPreview-102]
|
_ = x[actTransformPrompt-102]
|
||||||
_ = x[actPreviewTop-103]
|
_ = x[actTransformQuery-103]
|
||||||
_ = x[actPreviewBottom-104]
|
_ = x[actTransformSearch-104]
|
||||||
_ = x[actPreviewUp-105]
|
_ = x[actSearch-105]
|
||||||
_ = x[actPreviewDown-106]
|
_ = x[actPreview-106]
|
||||||
_ = x[actPreviewPageUp-107]
|
_ = x[actPreviewTop-107]
|
||||||
_ = x[actPreviewPageDown-108]
|
_ = x[actPreviewBottom-108]
|
||||||
_ = x[actPreviewHalfPageUp-109]
|
_ = x[actPreviewUp-109]
|
||||||
_ = x[actPreviewHalfPageDown-110]
|
_ = x[actPreviewDown-110]
|
||||||
_ = x[actPrevHistory-111]
|
_ = x[actPreviewPageUp-111]
|
||||||
_ = x[actPrevSelected-112]
|
_ = x[actPreviewPageDown-112]
|
||||||
_ = x[actPrint-113]
|
_ = x[actPreviewHalfPageUp-113]
|
||||||
_ = x[actPut-114]
|
_ = x[actPreviewHalfPageDown-114]
|
||||||
_ = x[actNextHistory-115]
|
_ = x[actPrevHistory-115]
|
||||||
_ = x[actNextSelected-116]
|
_ = x[actPrevSelected-116]
|
||||||
_ = x[actExecute-117]
|
_ = x[actPrint-117]
|
||||||
_ = x[actExecuteSilent-118]
|
_ = x[actPut-118]
|
||||||
_ = x[actExecuteMulti-119]
|
_ = x[actNextHistory-119]
|
||||||
_ = x[actSigStop-120]
|
_ = x[actNextSelected-120]
|
||||||
_ = x[actFirst-121]
|
_ = x[actExecute-121]
|
||||||
_ = x[actLast-122]
|
_ = x[actExecuteSilent-122]
|
||||||
_ = x[actReload-123]
|
_ = x[actExecuteMulti-123]
|
||||||
_ = x[actReloadSync-124]
|
_ = x[actSigStop-124]
|
||||||
_ = x[actDisableSearch-125]
|
_ = x[actFirst-125]
|
||||||
_ = x[actEnableSearch-126]
|
_ = x[actLast-126]
|
||||||
_ = x[actSelect-127]
|
_ = x[actReload-127]
|
||||||
_ = x[actDeselect-128]
|
_ = x[actReloadSync-128]
|
||||||
_ = x[actUnbind-129]
|
_ = x[actDisableSearch-129]
|
||||||
_ = x[actRebind-130]
|
_ = x[actEnableSearch-130]
|
||||||
_ = x[actToggleBind-131]
|
_ = x[actSelect-131]
|
||||||
_ = x[actBecome-132]
|
_ = x[actDeselect-132]
|
||||||
_ = x[actShowHeader-133]
|
_ = x[actUnbind-133]
|
||||||
_ = x[actHideHeader-134]
|
_ = x[actRebind-134]
|
||||||
_ = x[actBell-135]
|
_ = x[actToggleBind-135]
|
||||||
_ = x[actExclude-136]
|
_ = x[actBecome-136]
|
||||||
_ = x[actExcludeMulti-137]
|
_ = 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 {
|
func (i actionType) String() string {
|
||||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||||
|
@@ -39,7 +39,7 @@ func (r revision) compatible(other revision) bool {
|
|||||||
// Run starts fzf
|
// Run starts fzf
|
||||||
func Run(opts *Options) (int, error) {
|
func Run(opts *Options) (int, error) {
|
||||||
if opts.Filter == nil {
|
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)
|
return runTmux(os.Args, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
src/options.go
108
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)
|
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||||
--border[=STYLE] Draw border around the finder
|
--border[=STYLE] Draw border around the finder
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[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=LABEL Label to print on the border
|
||||||
--border-label-pos=COL Position of the border label
|
--border-label-pos=COL Position of the border label
|
||||||
[POSITIVE_INTEGER: columns from left|
|
[POSITIVE_INTEGER: columns from left|
|
||||||
@@ -140,7 +140,7 @@ Usage: fzf [options]
|
|||||||
--filepath-word Make word-wise movements respect path separators
|
--filepath-word Make word-wise movements respect path separators
|
||||||
--input-border[=STYLE] Draw border around the input section
|
--input-border[=STYLE] Draw border around the input section
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[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=LABEL Label to print on the input border
|
||||||
--input-label-pos=COL Position of the input label
|
--input-label-pos=COL Position of the input label
|
||||||
[POSITIVE_INTEGER: columns from left|
|
[POSITIVE_INTEGER: columns from left|
|
||||||
@@ -168,7 +168,7 @@ Usage: fzf [options]
|
|||||||
--header-first Print header before the prompt line
|
--header-first Print header before the prompt line
|
||||||
--header-border[=STYLE] Draw border around the header section
|
--header-border[=STYLE] Draw border around the header section
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[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]
|
--header-lines-border[=STYLE]
|
||||||
Display header from --header-lines with a separate border.
|
Display header from --header-lines with a separate border.
|
||||||
Pass 'none' to still separate it but without a border.
|
Pass 'none' to still separate it but without a border.
|
||||||
@@ -178,6 +178,17 @@ Usage: fzf [options]
|
|||||||
NEGATIVE_INTEGER: columns from right][:bottom]
|
NEGATIVE_INTEGER: columns from right][:bottom]
|
||||||
(default: 0 or center)
|
(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
|
SCRIPTING
|
||||||
-q, --query=STR Start the finder with the given query
|
-q, --query=STR Start the finder with the given query
|
||||||
-1, --select-1 Automatically select the only match
|
-1, --select-1 Automatically select the only match
|
||||||
@@ -599,6 +610,7 @@ type Options struct {
|
|||||||
Header []string
|
Header []string
|
||||||
HeaderLines int
|
HeaderLines int
|
||||||
HeaderFirst bool
|
HeaderFirst bool
|
||||||
|
Footer []string
|
||||||
Gap int
|
Gap int
|
||||||
GapLine *string
|
GapLine *string
|
||||||
Ellipsis *string
|
Ellipsis *string
|
||||||
@@ -610,8 +622,10 @@ type Options struct {
|
|||||||
InputBorderShape tui.BorderShape
|
InputBorderShape tui.BorderShape
|
||||||
HeaderBorderShape tui.BorderShape
|
HeaderBorderShape tui.BorderShape
|
||||||
HeaderLinesShape tui.BorderShape
|
HeaderLinesShape tui.BorderShape
|
||||||
|
FooterBorderShape tui.BorderShape
|
||||||
InputLabel labelOpts
|
InputLabel labelOpts
|
||||||
HeaderLabel labelOpts
|
HeaderLabel labelOpts
|
||||||
|
FooterLabel labelOpts
|
||||||
BorderLabel labelOpts
|
BorderLabel labelOpts
|
||||||
ListLabel labelOpts
|
ListLabel labelOpts
|
||||||
PreviewLabel labelOpts
|
PreviewLabel labelOpts
|
||||||
@@ -716,6 +730,7 @@ func defaultOptions() *Options {
|
|||||||
Header: make([]string, 0),
|
Header: make([]string, 0),
|
||||||
HeaderLines: 0,
|
HeaderLines: 0,
|
||||||
HeaderFirst: false,
|
HeaderFirst: false,
|
||||||
|
Footer: make([]string, 0),
|
||||||
Gap: 0,
|
Gap: 0,
|
||||||
Ellipsis: nil,
|
Ellipsis: nil,
|
||||||
Scrollbar: nil,
|
Scrollbar: nil,
|
||||||
@@ -880,12 +895,9 @@ func parseAlgo(str string) (algo.Algo, error) {
|
|||||||
return nil, errors.New("invalid algorithm (expected: v1 or v2)")
|
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 {
|
switch str {
|
||||||
case "line":
|
case "line":
|
||||||
if !allowLine {
|
|
||||||
return tui.BorderNone, errors.New("'line' is only allowed for preview border")
|
|
||||||
}
|
|
||||||
return tui.BorderLine, nil
|
return tui.BorderLine, nil
|
||||||
case "rounded":
|
case "rounded":
|
||||||
return tui.BorderRounded, nil
|
return tui.BorderRounded, nil
|
||||||
@@ -1348,6 +1360,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
mergeAttr(&theme.HeaderBorder)
|
mergeAttr(&theme.HeaderBorder)
|
||||||
case "header-label":
|
case "header-label":
|
||||||
mergeAttr(&theme.HeaderLabel)
|
mergeAttr(&theme.HeaderLabel)
|
||||||
|
case "footer-border":
|
||||||
|
mergeAttr(&theme.FooterBorder)
|
||||||
|
case "footer-label":
|
||||||
|
mergeAttr(&theme.FooterLabel)
|
||||||
case "spinner":
|
case "spinner":
|
||||||
mergeAttr(&theme.Spinner)
|
mergeAttr(&theme.Spinner)
|
||||||
case "info":
|
case "info":
|
||||||
@@ -1360,6 +1376,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
mergeAttr(&theme.Header)
|
mergeAttr(&theme.Header)
|
||||||
case "header-bg":
|
case "header-bg":
|
||||||
mergeAttr(&theme.HeaderBg)
|
mergeAttr(&theme.HeaderBg)
|
||||||
|
case "footer", "footer-fg":
|
||||||
|
mergeAttr(&theme.Footer)
|
||||||
|
case "footer-bg":
|
||||||
|
mergeAttr(&theme.FooterBg)
|
||||||
case "gap-line":
|
case "gap-line":
|
||||||
mergeAttr(&theme.GapLine)
|
mergeAttr(&theme.GapLine)
|
||||||
default:
|
default:
|
||||||
@@ -1415,7 +1435,7 @@ const (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
executeRegexp = regexp.MustCompile(
|
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("[,:]+")
|
splitRegexp = regexp.MustCompile("[,:]+")
|
||||||
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
||||||
}
|
}
|
||||||
@@ -1800,6 +1820,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actPreview
|
return actPreview
|
||||||
case "change-header":
|
case "change-header":
|
||||||
return actChangeHeader
|
return actChangeHeader
|
||||||
|
case "change-footer":
|
||||||
|
return actChangeFooter
|
||||||
case "change-list-label":
|
case "change-list-label":
|
||||||
return actChangeListLabel
|
return actChangeListLabel
|
||||||
case "change-border-label":
|
case "change-border-label":
|
||||||
@@ -1810,6 +1832,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actChangeInputLabel
|
return actChangeInputLabel
|
||||||
case "change-header-label":
|
case "change-header-label":
|
||||||
return actChangeHeaderLabel
|
return actChangeHeaderLabel
|
||||||
|
case "change-footer-label":
|
||||||
|
return actChangeFooterLabel
|
||||||
case "change-ghost":
|
case "change-ghost":
|
||||||
return actChangeGhost
|
return actChangeGhost
|
||||||
case "change-pointer":
|
case "change-pointer":
|
||||||
@@ -1850,6 +1874,10 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actTransformInputLabel
|
return actTransformInputLabel
|
||||||
case "transform-header-label":
|
case "transform-header-label":
|
||||||
return actTransformHeaderLabel
|
return actTransformHeaderLabel
|
||||||
|
case "transform-footer-label":
|
||||||
|
return actTransformFooterLabel
|
||||||
|
case "transform-footer":
|
||||||
|
return actTransformFooter
|
||||||
case "transform-header":
|
case "transform-header":
|
||||||
return actTransformHeader
|
return actTransformHeader
|
||||||
case "transform-ghost":
|
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 {
|
if opts.HeaderLines, err = nextInt("number of header lines required"); err != nil {
|
||||||
return err
|
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":
|
case "--header-first":
|
||||||
opts.HeaderFirst = true
|
opts.HeaderFirst = true
|
||||||
case "--no-header-first":
|
case "--no-header-first":
|
||||||
@@ -2773,7 +2809,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.Preview.border = tui.BorderNone
|
opts.Preview.border = tui.BorderNone
|
||||||
case "--preview-border":
|
case "--preview-border":
|
||||||
hasArg, arg := optionalNextString()
|
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
|
return err
|
||||||
}
|
}
|
||||||
case "--height":
|
case "--height":
|
||||||
@@ -2812,14 +2848,17 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.BorderShape = tui.BorderNone
|
opts.BorderShape = tui.BorderNone
|
||||||
case "--border":
|
case "--border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.BorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--list-border":
|
case "--list-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.ListBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.ListBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if opts.ListBorderShape == tui.BorderLine {
|
||||||
|
return errors.New("list border cannot be 'line'")
|
||||||
|
}
|
||||||
case "--no-list-border":
|
case "--no-list-border":
|
||||||
opts.ListBorderShape = tui.BorderNone
|
opts.ListBorderShape = tui.BorderNone
|
||||||
case "--no-list-label":
|
case "--no-list-label":
|
||||||
@@ -2841,14 +2880,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.HeaderBorderShape = tui.BorderNone
|
opts.HeaderBorderShape = tui.BorderNone
|
||||||
case "--header-border":
|
case "--header-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--no-header-lines-border":
|
case "--no-header-lines-border":
|
||||||
opts.HeaderLinesShape = tui.BorderNone
|
opts.HeaderLinesShape = tui.BorderNone
|
||||||
case "--header-lines-border":
|
case "--header-lines-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--no-header-label":
|
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 {
|
if err := parseLabelPosition(&opts.HeaderLabel, pos); err != nil {
|
||||||
return err
|
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":
|
case "--no-input-border":
|
||||||
opts.InputBorderShape = tui.BorderNone
|
opts.InputBorderShape = tui.BorderNone
|
||||||
case "--input-border":
|
case "--input-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.InputBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.InputBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--no-input-label":
|
case "--no-input-label":
|
||||||
@@ -3077,6 +3137,7 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
opts.ListBorderShape = tui.BorderUndefined
|
opts.ListBorderShape = tui.BorderUndefined
|
||||||
opts.InputBorderShape = tui.BorderUndefined
|
opts.InputBorderShape = tui.BorderUndefined
|
||||||
opts.HeaderBorderShape = tui.BorderUndefined
|
opts.HeaderBorderShape = tui.BorderUndefined
|
||||||
|
opts.FooterBorderShape = tui.BorderUndefined
|
||||||
opts.Preview.border = defaultBorderShape
|
opts.Preview.border = defaultBorderShape
|
||||||
opts.Preview.info = true
|
opts.Preview.info = true
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
@@ -3088,6 +3149,7 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
opts.ListBorderShape = tui.BorderUndefined
|
opts.ListBorderShape = tui.BorderUndefined
|
||||||
opts.InputBorderShape = tui.BorderUndefined
|
opts.InputBorderShape = tui.BorderUndefined
|
||||||
opts.HeaderBorderShape = tui.BorderUndefined
|
opts.HeaderBorderShape = tui.BorderUndefined
|
||||||
|
opts.FooterBorderShape = tui.BorderLine
|
||||||
opts.Preview.border = tui.BorderLine
|
opts.Preview.border = tui.BorderLine
|
||||||
opts.Preview.info = false
|
opts.Preview.info = false
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
@@ -3103,16 +3165,22 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
}
|
}
|
||||||
if len(tokens) == 2 && len(tokens[1]) > 0 {
|
if len(tokens) == 2 && len(tokens[1]) > 0 {
|
||||||
var err error
|
var err error
|
||||||
defaultBorderShape, err = parseBorder(tokens[1], false, false)
|
defaultBorderShape, err = parseBorder(tokens[1], false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.ListBorderShape = defaultBorderShape
|
if defaultBorderShape != tui.BorderLine {
|
||||||
|
opts.ListBorderShape = defaultBorderShape
|
||||||
|
}
|
||||||
opts.InputBorderShape = defaultBorderShape
|
opts.InputBorderShape = defaultBorderShape
|
||||||
opts.HeaderBorderShape = defaultBorderShape
|
opts.HeaderBorderShape = defaultBorderShape
|
||||||
|
opts.FooterBorderShape = defaultBorderShape
|
||||||
opts.Preview.border = defaultBorderShape
|
opts.Preview.border = defaultBorderShape
|
||||||
|
if defaultBorderShape == tui.BorderLine {
|
||||||
|
opts.BorderShape = defaultBorderShape
|
||||||
|
}
|
||||||
opts.Preview.info = true
|
opts.Preview.info = true
|
||||||
opts.InfoStyle = infoInlineRight
|
opts.InfoStyle = infoInlineRight
|
||||||
opts.Theme.Gutter = tui.NewColorAttr()
|
opts.Theme.Gutter = tui.NewColorAttr()
|
||||||
@@ -3185,6 +3253,10 @@ func noSeparatorLine(style infoStyle, separator bool) bool {
|
|||||||
return false
|
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 {
|
func (opts *Options) noSeparatorLine() bool {
|
||||||
if opts.Inputless {
|
if opts.Inputless {
|
||||||
return true
|
return true
|
||||||
@@ -3216,6 +3288,10 @@ func postProcessOptions(opts *Options) error {
|
|||||||
opts.HeaderBorderShape = tui.BorderNone
|
opts.HeaderBorderShape = tui.BorderNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.FooterBorderShape == tui.BorderUndefined {
|
||||||
|
opts.FooterBorderShape = tui.BorderLine
|
||||||
|
}
|
||||||
|
|
||||||
if opts.HeaderLinesShape == tui.BorderNone {
|
if opts.HeaderLinesShape == tui.BorderNone {
|
||||||
opts.HeaderLinesShape = tui.BorderPhantom
|
opts.HeaderLinesShape = tui.BorderPhantom
|
||||||
}
|
}
|
||||||
|
232
src/terminal.go
232
src/terminal.go
@@ -180,14 +180,18 @@ type itemLine struct {
|
|||||||
other bool
|
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) {
|
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}
|
t.prevLines[line] = itemLine{valid: true, firstLine: line, empty: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) markOtherLine(line int) {
|
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}
|
t.prevLines[line] = itemLine{valid: true, firstLine: line, other: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,6 +258,9 @@ type Terminal struct {
|
|||||||
headerLabel labelPrinter
|
headerLabel labelPrinter
|
||||||
headerLabelLen int
|
headerLabelLen int
|
||||||
headerLabelOpts labelOpts
|
headerLabelOpts labelOpts
|
||||||
|
footerLabel labelPrinter
|
||||||
|
footerLabelLen int
|
||||||
|
footerLabelOpts labelOpts
|
||||||
pointer string
|
pointer string
|
||||||
pointerLen int
|
pointerLen int
|
||||||
pointerEmpty string
|
pointerEmpty string
|
||||||
@@ -301,6 +308,7 @@ type Terminal struct {
|
|||||||
headerLines int
|
headerLines int
|
||||||
header []string
|
header []string
|
||||||
header0 []string
|
header0 []string
|
||||||
|
footer []string
|
||||||
ellipsis string
|
ellipsis string
|
||||||
scrollbar string
|
scrollbar string
|
||||||
previewScrollbar string
|
previewScrollbar string
|
||||||
@@ -322,6 +330,7 @@ type Terminal struct {
|
|||||||
inputBorderShape tui.BorderShape
|
inputBorderShape tui.BorderShape
|
||||||
headerBorderShape tui.BorderShape
|
headerBorderShape tui.BorderShape
|
||||||
headerLinesShape tui.BorderShape
|
headerLinesShape tui.BorderShape
|
||||||
|
footerBorderShape tui.BorderShape
|
||||||
listLabel labelPrinter
|
listLabel labelPrinter
|
||||||
listLabelLen int
|
listLabelLen int
|
||||||
listLabelOpts labelOpts
|
listLabelOpts labelOpts
|
||||||
@@ -337,6 +346,8 @@ type Terminal struct {
|
|||||||
headerBorder tui.Window
|
headerBorder tui.Window
|
||||||
headerLinesWindow tui.Window
|
headerLinesWindow tui.Window
|
||||||
headerLinesBorder tui.Window
|
headerLinesBorder tui.Window
|
||||||
|
footerWindow tui.Window
|
||||||
|
footerBorder tui.Window
|
||||||
wborder tui.Window
|
wborder tui.Window
|
||||||
pborder tui.Window
|
pborder tui.Window
|
||||||
pwindow tui.Window
|
pwindow tui.Window
|
||||||
@@ -426,6 +437,7 @@ const (
|
|||||||
reqPrompt util.EventType = iota
|
reqPrompt util.EventType = iota
|
||||||
reqInfo
|
reqInfo
|
||||||
reqHeader
|
reqHeader
|
||||||
|
reqFooter
|
||||||
reqList
|
reqList
|
||||||
reqJump
|
reqJump
|
||||||
reqActivate
|
reqActivate
|
||||||
@@ -434,6 +446,7 @@ const (
|
|||||||
reqResize
|
reqResize
|
||||||
reqRedrawInputLabel
|
reqRedrawInputLabel
|
||||||
reqRedrawHeaderLabel
|
reqRedrawHeaderLabel
|
||||||
|
reqRedrawFooterLabel
|
||||||
reqRedrawListLabel
|
reqRedrawListLabel
|
||||||
reqRedrawBorderLabel
|
reqRedrawBorderLabel
|
||||||
reqRedrawPreviewLabel
|
reqRedrawPreviewLabel
|
||||||
@@ -479,7 +492,9 @@ const (
|
|||||||
actChangeBorderLabel
|
actChangeBorderLabel
|
||||||
actChangeGhost
|
actChangeGhost
|
||||||
actChangeHeader
|
actChangeHeader
|
||||||
|
actChangeFooter
|
||||||
actChangeHeaderLabel
|
actChangeHeaderLabel
|
||||||
|
actChangeFooterLabel
|
||||||
actChangeInputLabel
|
actChangeInputLabel
|
||||||
actChangeListLabel
|
actChangeListLabel
|
||||||
actChangeMulti
|
actChangeMulti
|
||||||
@@ -550,7 +565,9 @@ const (
|
|||||||
actTransformBorderLabel
|
actTransformBorderLabel
|
||||||
actTransformGhost
|
actTransformGhost
|
||||||
actTransformHeader
|
actTransformHeader
|
||||||
|
actTransformFooter
|
||||||
actTransformHeaderLabel
|
actTransformHeaderLabel
|
||||||
|
actTransformFooterLabel
|
||||||
actTransformInputLabel
|
actTransformInputLabel
|
||||||
actTransformListLabel
|
actTransformListLabel
|
||||||
actTransformNth
|
actTransformNth
|
||||||
@@ -907,6 +924,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
inputBorderShape: opts.InputBorderShape,
|
inputBorderShape: opts.InputBorderShape,
|
||||||
headerBorderShape: opts.HeaderBorderShape,
|
headerBorderShape: opts.HeaderBorderShape,
|
||||||
headerLinesShape: opts.HeaderLinesShape,
|
headerLinesShape: opts.HeaderLinesShape,
|
||||||
|
footerBorderShape: opts.FooterBorderShape,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
listLabel: nil,
|
listLabel: nil,
|
||||||
listLabelOpts: opts.ListLabel,
|
listLabelOpts: opts.ListLabel,
|
||||||
@@ -918,6 +936,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
inputLabelOpts: opts.InputLabel,
|
inputLabelOpts: opts.InputLabel,
|
||||||
headerLabel: nil,
|
headerLabel: nil,
|
||||||
headerLabelOpts: opts.HeaderLabel,
|
headerLabelOpts: opts.HeaderLabel,
|
||||||
|
footerLabel: nil,
|
||||||
|
footerLabelOpts: opts.FooterLabel,
|
||||||
cleanExit: opts.ClearOnExit,
|
cleanExit: opts.ClearOnExit,
|
||||||
executor: executor,
|
executor: executor,
|
||||||
paused: opts.Phony,
|
paused: opts.Phony,
|
||||||
@@ -929,6 +949,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
headerLines: opts.HeaderLines,
|
headerLines: opts.HeaderLines,
|
||||||
gap: opts.Gap,
|
gap: opts.Gap,
|
||||||
header: []string{},
|
header: []string{},
|
||||||
|
footer: opts.Footer,
|
||||||
header0: opts.Header,
|
header0: opts.Header,
|
||||||
ansi: opts.Ansi,
|
ansi: opts.Ansi,
|
||||||
nthAttr: opts.Theme.Nth.Attr,
|
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.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
|
||||||
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(opts.InputLabel.label, &tui.ColInputLabel, 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.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
|
// Disable separator by default if input border is set
|
||||||
if opts.Separator == nil && !t.inputBorderShape.Visible() || opts.Separator != nil && len(*opts.Separator) > 0 {
|
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
|
extra += t.headerLines
|
||||||
}
|
}
|
||||||
|
if len(t.footer) > 0 {
|
||||||
|
extra += borderLines(t.footerBorderShape)
|
||||||
|
extra += len(t.footer)
|
||||||
|
}
|
||||||
return extra
|
return extra
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1475,6 +1546,16 @@ func (t *Terminal) changeHeader(header string) bool {
|
|||||||
return needFullRedraw
|
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
|
// UpdateHeader updates the header
|
||||||
func (t *Terminal) UpdateHeader(header []string) {
|
func (t *Terminal) UpdateHeader(header []string) {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
@@ -1835,6 +1916,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
if t.headerBorder != nil {
|
if t.headerBorder != nil {
|
||||||
t.headerBorder = nil
|
t.headerBorder = nil
|
||||||
}
|
}
|
||||||
|
if t.footerWindow != nil {
|
||||||
|
t.footerWindow = nil
|
||||||
|
}
|
||||||
|
if t.footerBorder != nil {
|
||||||
|
t.footerBorder = nil
|
||||||
|
}
|
||||||
if t.headerLinesWindow != nil {
|
if t.headerLinesWindow != nil {
|
||||||
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
|
// Adjust position and size of the list window if input border is set
|
||||||
inputBorderHeight := 0
|
inputBorderHeight := 0
|
||||||
availableLines := height
|
availableLines := height
|
||||||
|
|
||||||
shift := 0
|
shift := 0
|
||||||
shrink := 0
|
shrink := 0
|
||||||
hasHeaderWindow := t.hasHeaderWindow()
|
hasHeaderWindow := t.hasHeaderWindow()
|
||||||
|
hasFooterWindow := len(t.footer) > 0
|
||||||
hasHeaderLinesWindow, headerLinesShape := t.determineHeaderLinesShape()
|
hasHeaderLinesWindow, headerLinesShape := t.determineHeaderLinesShape()
|
||||||
hasInputWindow := !t.inputless && (t.inputBorderShape.Visible() || hasHeaderWindow || hasHeaderLinesWindow)
|
hasInputWindow := !t.inputless && (t.inputBorderShape.Visible() || hasHeaderWindow || hasHeaderLinesWindow)
|
||||||
|
inputWindowHeight := 2
|
||||||
|
if t.noSeparatorLine() {
|
||||||
|
inputWindowHeight--
|
||||||
|
}
|
||||||
if hasInputWindow {
|
if hasInputWindow {
|
||||||
inputWindowHeight := 2
|
inputBorderHeight = util.Constrain(borderLines(t.inputBorderShape)+inputWindowHeight, 0, availableLines)
|
||||||
if t.noSeparatorLine() {
|
|
||||||
inputWindowHeight--
|
|
||||||
}
|
|
||||||
inputBorderHeight = util.Min(availableLines, borderLines(t.inputBorderShape)+inputWindowHeight)
|
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
shift = inputBorderHeight
|
shift = inputBorderHeight
|
||||||
shrink = inputBorderHeight
|
shrink = inputBorderHeight
|
||||||
@@ -1907,6 +1996,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
shrink = inputBorderHeight
|
shrink = inputBorderHeight
|
||||||
}
|
}
|
||||||
availableLines -= 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
|
// 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 {
|
if hasHeaderLinesWindow {
|
||||||
headerWindowHeight -= t.headerLines
|
headerWindowHeight -= t.headerLines
|
||||||
}
|
}
|
||||||
headerBorderHeight = util.Min(availableLines, borderLines(t.headerBorderShape)+headerWindowHeight)
|
headerBorderHeight = util.Constrain(borderLines(t.headerBorderShape)+headerWindowHeight, 0, availableLines)
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
shift += headerBorderHeight
|
shift += headerBorderHeight
|
||||||
shrink += headerBorderHeight
|
shrink += headerBorderHeight
|
||||||
@@ -1928,7 +2028,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
|
|
||||||
headerLinesHeight := 0
|
headerLinesHeight := 0
|
||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow {
|
||||||
headerLinesHeight = util.Min(availableLines, borderLines(headerLinesShape)+t.headerLines)
|
headerLinesHeight = util.Constrain(borderLines(headerLinesShape)+t.headerLines, 0, availableLines)
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
shift += headerLinesHeight
|
shift += headerLinesHeight
|
||||||
shrink += headerLinesHeight
|
shrink += headerLinesHeight
|
||||||
@@ -1938,6 +2038,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
availableLines -= headerLinesHeight
|
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
|
// Set up list border
|
||||||
hasListBorder := t.listBorderShape.Visible()
|
hasListBorder := t.listBorderShape.Visible()
|
||||||
innerWidth := width
|
innerWidth := width
|
||||||
@@ -2041,13 +2152,8 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
width++
|
width++
|
||||||
}
|
}
|
||||||
|
|
||||||
maxPreviewLines := availableLines
|
pheight = util.Constrain(pheight, minPreviewHeight, availableLines)
|
||||||
if t.wborder != nil {
|
|
||||||
maxPreviewLines -= t.wborder.Height()
|
|
||||||
} else {
|
|
||||||
maxPreviewLines -= util.Max(0, innerHeight-pheight-shrink)
|
|
||||||
}
|
|
||||||
pheight = util.Min(pheight, maxPreviewLines)
|
|
||||||
if previewOpts.position == posUp {
|
if previewOpts.position == posUp {
|
||||||
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
|
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
@@ -2210,7 +2316,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
case layoutDefault:
|
case layoutDefault:
|
||||||
btop = w.Top() + w.Height() + headerBorderHeight + headerLinesHeight
|
btop = w.Top() + w.Height() + headerBorderHeight + headerLinesHeight
|
||||||
case layoutReverse:
|
case layoutReverse:
|
||||||
btop = w.Top() - shrink
|
btop = w.Top() - shrink + footerBorderHeight
|
||||||
case layoutReverseList:
|
case layoutReverseList:
|
||||||
btop = w.Top() + w.Height() + headerBorderHeight
|
btop = w.Top() + w.Height() + headerBorderHeight
|
||||||
}
|
}
|
||||||
@@ -2238,7 +2344,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
var btop int
|
var btop int
|
||||||
if hasInputWindow && t.headerFirst {
|
if hasInputWindow && t.headerFirst {
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
btop = w.Top() - shrink
|
btop = w.Top() - shrink + footerBorderHeight
|
||||||
} else if t.layout == layoutReverseList {
|
} else if t.layout == layoutReverseList {
|
||||||
btop = w.Top() + w.Height() + inputBorderHeight
|
btop = w.Top() + w.Height() + inputBorderHeight
|
||||||
} else {
|
} else {
|
||||||
@@ -2294,12 +2400,31 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
t.headerLinesWindow = createInnerWindow(t.headerLinesBorder, headerLinesShape, tui.WindowHeader, 0)
|
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
|
// Print border label
|
||||||
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, false)
|
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, false)
|
||||||
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
|
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
|
||||||
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.Border(), false)
|
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.Border(), false)
|
||||||
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, 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.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) {
|
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:
|
case layoutDefault:
|
||||||
y = h - y - 1
|
y = h - y - 1
|
||||||
case layoutReverseList:
|
case layoutReverseList:
|
||||||
if t.window == t.inputWindow || t.window == t.headerWindow {
|
if !t.inListWindow() && t.window != t.headerLinesWindow {
|
||||||
// From bottom to top
|
// From bottom to top
|
||||||
y = h - y - 1
|
y = h - y - 1
|
||||||
} else {
|
} else {
|
||||||
@@ -2690,8 +2815,10 @@ func (t *Terminal) resizeIfNeeded() bool {
|
|||||||
if t.hasHeaderLinesWindow() {
|
if t.hasHeaderLinesWindow() {
|
||||||
primaryHeaderLines -= t.headerLines
|
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()
|
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()) ||
|
(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 || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) ||
|
||||||
!needHeaderLinesWindow && t.headerLinesWindow != nil {
|
!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 {
|
func (t *Terminal) headerIndent(borderShape tui.BorderShape) int {
|
||||||
indentSize := t.pointerLen + t.markerLen
|
indentSize := t.pointerLen + t.markerLen
|
||||||
if t.listBorderShape.HasLeft() {
|
if t.listBorderShape.HasLeft() {
|
||||||
@@ -2792,7 +2954,7 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) canSpanMultiLines() bool {
|
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) {
|
func (t *Terminal) renderBar(line int, barRange [2]int) {
|
||||||
@@ -3767,6 +3929,7 @@ func (t *Terminal) printAll() {
|
|||||||
t.printPrompt()
|
t.printPrompt()
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
t.printHeader()
|
t.printHeader()
|
||||||
|
t.printFooter()
|
||||||
t.printPreview()
|
t.printPreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4515,6 +4678,7 @@ func (t *Terminal) Loop() error {
|
|||||||
t.reqBox.Set(reqPrompt, nil)
|
t.reqBox.Set(reqPrompt, nil)
|
||||||
t.reqBox.Set(reqInfo, nil)
|
t.reqBox.Set(reqInfo, nil)
|
||||||
t.reqBox.Set(reqHeader, nil)
|
t.reqBox.Set(reqHeader, nil)
|
||||||
|
t.reqBox.Set(reqFooter, nil)
|
||||||
if t.initDelay > 0 {
|
if t.initDelay > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
timer := time.NewTimer(t.initDelay)
|
timer := time.NewTimer(t.initDelay)
|
||||||
@@ -4797,6 +4961,10 @@ func (t *Terminal) Loop() error {
|
|||||||
if !t.resizeIfNeeded() {
|
if !t.resizeIfNeeded() {
|
||||||
t.printHeader()
|
t.printHeader()
|
||||||
}
|
}
|
||||||
|
case reqFooter:
|
||||||
|
if !t.resizeIfNeeded() {
|
||||||
|
t.printFooter()
|
||||||
|
}
|
||||||
case reqActivate:
|
case reqActivate:
|
||||||
t.suppress = false
|
t.suppress = false
|
||||||
if t.hasPreviewer() {
|
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)
|
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
|
||||||
case reqRedrawHeaderLabel:
|
case reqRedrawHeaderLabel:
|
||||||
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, true)
|
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:
|
case reqRedrawListLabel:
|
||||||
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
|
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
|
||||||
case reqRedrawBorderLabel:
|
case reqRedrawBorderLabel:
|
||||||
@@ -4996,7 +5166,7 @@ func (t *Terminal) Loop() error {
|
|||||||
}
|
}
|
||||||
updatePreviewWindow := func(forcePreview bool) {
|
updatePreviewWindow := func(forcePreview bool) {
|
||||||
t.resizeWindows(forcePreview, false)
|
t.resizeWindows(forcePreview, false)
|
||||||
req(reqPrompt, reqList, reqInfo, reqHeader)
|
req(reqPrompt, reqList, reqInfo, reqHeader, reqFooter)
|
||||||
}
|
}
|
||||||
toggle := func() bool {
|
toggle := func() bool {
|
||||||
current := t.currentItem()
|
current := t.currentItem()
|
||||||
@@ -5271,6 +5441,16 @@ func (t *Terminal) Loop() error {
|
|||||||
} else {
|
} else {
|
||||||
req(reqHeader)
|
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:
|
case actChangeHeaderLabel, actTransformHeaderLabel:
|
||||||
label := a.a
|
label := a.a
|
||||||
if a.t == actTransformHeaderLabel {
|
if a.t == actTransformHeaderLabel {
|
||||||
@@ -5279,6 +5459,14 @@ func (t *Terminal) Loop() error {
|
|||||||
t.headerLabelOpts.label = label
|
t.headerLabelOpts.label = label
|
||||||
t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false)
|
t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false)
|
||||||
req(reqRedrawHeaderLabel)
|
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:
|
case actChangeInputLabel, actTransformInputLabel:
|
||||||
label := a.a
|
label := a.a
|
||||||
if a.t == actTransformInputLabel {
|
if a.t == actTransformInputLabel {
|
||||||
|
@@ -11,10 +11,14 @@ func runTmux(args []string, opts *Options) (int, error) {
|
|||||||
// Prepare arguments
|
// Prepare arguments
|
||||||
fzf, rest := args[0], args[1:]
|
fzf, rest := args[0], args[1:]
|
||||||
args = []string{"--bind=ctrl-z:ignore"}
|
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`
|
// We append --border option at the end, because `--style=full:STYLE`
|
||||||
// may have changed the default border 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() {
|
if opts.Tmux.border && opts.Margin == defaultMargin() {
|
||||||
args = append(args, "--margin=0,1")
|
args = append(args, "--margin=0,1")
|
||||||
|
@@ -829,11 +829,14 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, wind
|
|||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
w.fg = r.theme.Header.Color
|
w.fg = r.theme.Header.Color
|
||||||
w.bg = r.theme.HeaderBg.Color
|
w.bg = r.theme.HeaderBg.Color
|
||||||
|
case WindowFooter:
|
||||||
|
w.fg = r.theme.Footer.Color
|
||||||
|
w.bg = r.theme.FooterBg.Color
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
w.fg = r.theme.PreviewFg.Color
|
w.fg = r.theme.PreviewFg.Color
|
||||||
w.bg = r.theme.PreviewBg.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
|
// fzf --color bg:blue --border --padding 1,2
|
||||||
w.Erase()
|
w.Erase()
|
||||||
}
|
}
|
||||||
@@ -889,6 +892,8 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
@@ -914,6 +919,8 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
@@ -941,6 +948,8 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
|
@@ -600,6 +600,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
|||||||
normal = ColNormal
|
normal = ColNormal
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
normal = ColHeader
|
normal = ColHeader
|
||||||
|
case WindowFooter:
|
||||||
|
normal = ColFooter
|
||||||
case WindowInput:
|
case WindowInput:
|
||||||
normal = ColInput
|
normal = ColInput
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
@@ -865,6 +867,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
|||||||
style = ColListBorder.style()
|
style = ColListBorder.style()
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
style = ColHeaderBorder.style()
|
style = ColHeaderBorder.style()
|
||||||
|
case WindowFooter:
|
||||||
|
style = ColFooterBorder.style()
|
||||||
case WindowInput:
|
case WindowInput:
|
||||||
style = ColInputBorder.style()
|
style = ColInputBorder.style()
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
|
@@ -359,6 +359,10 @@ type ColorTheme struct {
|
|||||||
HeaderBg ColorAttr
|
HeaderBg ColorAttr
|
||||||
HeaderBorder ColorAttr
|
HeaderBorder ColorAttr
|
||||||
HeaderLabel ColorAttr
|
HeaderLabel ColorAttr
|
||||||
|
Footer ColorAttr
|
||||||
|
FooterBg ColorAttr
|
||||||
|
FooterBorder ColorAttr
|
||||||
|
FooterLabel ColorAttr
|
||||||
Separator ColorAttr
|
Separator ColorAttr
|
||||||
Scrollbar ColorAttr
|
Scrollbar ColorAttr
|
||||||
Border ColorAttr
|
Border ColorAttr
|
||||||
@@ -612,6 +616,7 @@ const (
|
|||||||
WindowPreview
|
WindowPreview
|
||||||
WindowInput
|
WindowInput
|
||||||
WindowHeader
|
WindowHeader
|
||||||
|
WindowFooter
|
||||||
)
|
)
|
||||||
|
|
||||||
type Renderer interface {
|
type Renderer interface {
|
||||||
@@ -720,6 +725,9 @@ var (
|
|||||||
ColHeader ColorPair
|
ColHeader ColorPair
|
||||||
ColHeaderBorder ColorPair
|
ColHeaderBorder ColorPair
|
||||||
ColHeaderLabel ColorPair
|
ColHeaderLabel ColorPair
|
||||||
|
ColFooter ColorPair
|
||||||
|
ColFooterBorder ColorPair
|
||||||
|
ColFooterLabel ColorPair
|
||||||
ColSeparator ColorPair
|
ColSeparator ColorPair
|
||||||
ColScrollbar ColorPair
|
ColScrollbar ColorPair
|
||||||
ColGapLine ColorPair
|
ColGapLine ColorPair
|
||||||
@@ -758,6 +766,7 @@ func EmptyTheme() *ColorTheme {
|
|||||||
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Marker: ColorAttr{colUndefined, AttrUndefined},
|
Marker: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Header: ColorAttr{colUndefined, AttrUndefined},
|
Header: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Footer: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Border: ColorAttr{colUndefined, AttrUndefined},
|
Border: ColorAttr{colUndefined, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
ListLabel: ColorAttr{colUndefined, AttrUndefined},
|
ListLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
@@ -778,6 +787,9 @@ func EmptyTheme() *ColorTheme {
|
|||||||
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -825,6 +837,9 @@ func NoColorTheme() *ColorTheme {
|
|||||||
HeaderBg: ColorAttr{colDefault, AttrUndefined},
|
HeaderBg: ColorAttr{colDefault, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
|
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
|
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
GapLine: ColorAttr{colDefault, AttrUndefined},
|
GapLine: ColorAttr{colDefault, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -852,6 +867,7 @@ func init() {
|
|||||||
Cursor: ColorAttr{colRed, AttrUndefined},
|
Cursor: ColorAttr{colRed, AttrUndefined},
|
||||||
Marker: ColorAttr{colMagenta, AttrUndefined},
|
Marker: ColorAttr{colMagenta, AttrUndefined},
|
||||||
Header: ColorAttr{colCyan, AttrUndefined},
|
Header: ColorAttr{colCyan, AttrUndefined},
|
||||||
|
Footer: ColorAttr{colCyan, AttrUndefined},
|
||||||
Border: ColorAttr{colBlack, AttrUndefined},
|
Border: ColorAttr{colBlack, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{colWhite, AttrUndefined},
|
BorderLabel: ColorAttr{colWhite, AttrUndefined},
|
||||||
Ghost: ColorAttr{colUndefined, Dim},
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
@@ -869,6 +885,12 @@ func init() {
|
|||||||
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputLabel: 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},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -893,6 +915,7 @@ func init() {
|
|||||||
Cursor: ColorAttr{161, AttrUndefined},
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Marker: ColorAttr{168, AttrUndefined},
|
Marker: ColorAttr{168, AttrUndefined},
|
||||||
Header: ColorAttr{109, AttrUndefined},
|
Header: ColorAttr{109, AttrUndefined},
|
||||||
|
Footer: ColorAttr{109, AttrUndefined},
|
||||||
Border: ColorAttr{59, AttrUndefined},
|
Border: ColorAttr{59, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{145, AttrUndefined},
|
BorderLabel: ColorAttr{145, AttrUndefined},
|
||||||
Ghost: ColorAttr{colUndefined, Dim},
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
@@ -910,6 +933,12 @@ func init() {
|
|||||||
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputLabel: 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},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -934,6 +963,7 @@ func init() {
|
|||||||
Cursor: ColorAttr{161, AttrUndefined},
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Marker: ColorAttr{168, AttrUndefined},
|
Marker: ColorAttr{168, AttrUndefined},
|
||||||
Header: ColorAttr{31, AttrUndefined},
|
Header: ColorAttr{31, AttrUndefined},
|
||||||
|
Footer: ColorAttr{31, AttrUndefined},
|
||||||
Border: ColorAttr{145, AttrUndefined},
|
Border: ColorAttr{145, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{59, AttrUndefined},
|
BorderLabel: ColorAttr{59, AttrUndefined},
|
||||||
Ghost: ColorAttr{colUndefined, Dim},
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
@@ -954,6 +984,9 @@ func init() {
|
|||||||
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: 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.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
||||||
theme.Marker = o(baseTheme.Marker, theme.Marker)
|
theme.Marker = o(baseTheme.Marker, theme.Marker)
|
||||||
theme.Header = o(baseTheme.Header, theme.Header)
|
theme.Header = o(baseTheme.Header, theme.Header)
|
||||||
|
theme.Footer = o(baseTheme.Footer, theme.Footer)
|
||||||
theme.Border = o(baseTheme.Border, theme.Border)
|
theme.Border = o(baseTheme.Border, theme.Border)
|
||||||
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
|
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.HeaderBorder = o(theme.Border, theme.HeaderBorder)
|
||||||
theme.HeaderLabel = o(theme.BorderLabel, theme.HeaderLabel)
|
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)
|
initPalette(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1095,6 +1133,9 @@ func initPalette(theme *ColorTheme) {
|
|||||||
ColHeader = pair(theme.Header, theme.HeaderBg)
|
ColHeader = pair(theme.Header, theme.HeaderBg)
|
||||||
ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
|
ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
|
||||||
ColHeaderLabel = pair(theme.HeaderLabel, 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 {
|
func runeWidth(r rune) int {
|
||||||
|
@@ -97,24 +97,12 @@ func Min32(first int32, second int32) int32 {
|
|||||||
|
|
||||||
// Constrain32 limits the given 32-bit integer with the upper and lower bounds
|
// Constrain32 limits the given 32-bit integer with the upper and lower bounds
|
||||||
func Constrain32(val int32, min int32, max int32) int32 {
|
func Constrain32(val int32, min int32, max int32) int32 {
|
||||||
if val < min {
|
return Max32(Min32(val, max), min)
|
||||||
return min
|
|
||||||
}
|
|
||||||
if val > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constrain limits the given integer with the upper and lower bounds
|
// Constrain limits the given integer with the upper and lower bounds
|
||||||
func Constrain(val int, min int, max int) int {
|
func Constrain(val int, min int, max int) int {
|
||||||
if val < min {
|
return Max(Min(val, max), min)
|
||||||
return min
|
|
||||||
}
|
|
||||||
if val > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AsUint16(val int) uint16 {
|
func AsUint16(val int) uint16 {
|
||||||
|
@@ -979,6 +979,126 @@ class TestLayout < TestInteractive
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_layout_default_with_footer
|
||||||
|
prefix = %[
|
||||||
|
seq 3 | #{FZF} --no-list-border --height ~100% \
|
||||||
|
--border sharp --footer "$(seq 201 202)" --footer-label FOOT --footer-label-pos 3 \
|
||||||
|
--header-label HEAD --header-label-pos 3:bottom \
|
||||||
|
--bind 'space:transform-footer-label(echo foot)+change-header-label(head)'
|
||||||
|
].strip + ' '
|
||||||
|
suffixes = [
|
||||||
|
%(),
|
||||||
|
%[--header "$(seq 101 102)"],
|
||||||
|
%[--header "$(seq 101 102)" --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2 --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --input-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --no-input],
|
||||||
|
%[--header "$(seq 101 102)" --footer-border sharp --input-border line],
|
||||||
|
%[--header "$(seq 101 102)" --style full:sharp --header-first]
|
||||||
|
]
|
||||||
|
output = <<~BLOCK
|
||||||
|
┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌───────── ┌──────── ┌──────── ┌─────────
|
||||||
|
│ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ ┌─FOOT─ │ ┌─FOOT──
|
||||||
|
│ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ │ 201 │ │ 201
|
||||||
|
│ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT── │ ──FOOT─ │ │ 202 │ │ 202
|
||||||
|
│ 3 │ 3 │ 3 │ > 3 │ > 3 │ 3 │ 3 │ > 3 │ > 3 │ > 3 │ > 3 │ └────── │ └───────
|
||||||
|
│ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ ┌────── │ ┌────── │ ┌─────── │ ┌────── │ 3 │ ┌───────
|
||||||
|
│ > 1 │ > 1 │ > 1 │ 1 │ 1 │ > 1 │ > 1 │ │ 2 │ │ 2 │ │ 2 │ │ 2 │ 2 │ │ 3
|
||||||
|
│ 3/3 ─ │ 101 │ 3/3 ─ │ 101 │ 1/1 ─ │ ┌────── │ 3/3 ─ │ │ 1 │ │ 1 │ │ 1 │ │ 1 │ > 1 │ │ 2
|
||||||
|
│ > │ 102 │ > │ 102 │ > │ │ 101 │ > │ │ 101 │ └────── │ └─────── │ └────── │ 101 │ │ > 1
|
||||||
|
└──────── │ 3/3 ─ │ 101 │ 1/1 ─ │ 101 │ │ 102 │ ┌────── │ │ 102 │ ┌────── │ ┌─────── │ ┌────── │ 102 │ └───────
|
||||||
|
│ > │ 102 │ > │ 102 │ └─HEAD─ │ │ 101 │ └─HEAD─ │ │ 101 │ │ 1/1 │ │ 101 │ ─────── │ ┌───────
|
||||||
|
└──────── └──────── └──────── └──────── │ 3/3 ─ │ │ 102 │ 1/1 ─ │ │ 102 │ │ > │ │ 102 │ 3/3 │ │ >
|
||||||
|
│ > │ └─HEAD─ │ > │ └─HEAD─ │ └─────── │ └─HEAD─ │ > │ └───────
|
||||||
|
└──────── └──────── └──────── │ 1/1 ─ │ ┌─────── └──────── └──────── │ ┌───────
|
||||||
|
│ > │ │ 101 │ │ 101
|
||||||
|
└──────── │ │ 102 │ │ 102
|
||||||
|
│ └─HEAD── │ └─HEAD──
|
||||||
|
└───────── └─────────
|
||||||
|
BLOCK
|
||||||
|
|
||||||
|
expects = []
|
||||||
|
output.each_line.first.scan(/\S+/) do
|
||||||
|
offset = Regexp.last_match.offset(0)
|
||||||
|
expects << output.lines.filter_map { it[offset[0]...offset[1]]&.strip }.take_while { !it.empty? }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
suffixes.zip(expects).each do |suffix, block|
|
||||||
|
tmux.send_keys(prefix + suffix, :Enter)
|
||||||
|
tmux.until { assert_block(block, it) }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { assert_block(block.downcase, it) }
|
||||||
|
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_layout_reverse_list_with_footer
|
||||||
|
prefix = %[
|
||||||
|
seq 3 | #{FZF} --layout reverse-list --no-list-border --height ~100% \
|
||||||
|
--border sharp --footer "$(seq 201 202)" --footer-label FOOT --footer-label-pos 3 \
|
||||||
|
--header-label HEAD --header-label-pos 3:bottom \
|
||||||
|
--bind 'space:transform-footer-label(echo foot)+change-header-label(head)'
|
||||||
|
].strip + ' '
|
||||||
|
suffixes = [
|
||||||
|
%(),
|
||||||
|
%[--header "$(seq 101 102)"],
|
||||||
|
%[--header "$(seq 101 102)" --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2 --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --input-border sharp],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --no-input],
|
||||||
|
%[--header "$(seq 101 102)" --footer-border sharp --input-border line],
|
||||||
|
%[--header "$(seq 101 102)" --style full:sharp --header-first]
|
||||||
|
]
|
||||||
|
output = <<~BLOCK
|
||||||
|
┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌───────── ┌──────── ┌──────── ┌─────────
|
||||||
|
│ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ 201 │ ┌─FOOT─ │ ┌─FOOT──
|
||||||
|
│ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ 202 │ │ 201 │ │ 201
|
||||||
|
│ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT── │ ──FOOT─ │ │ 202 │ │ 202
|
||||||
|
│ > 1 │ > 1 │ > 1 │ 1 │ 1 │ > 1 │ > 1 │ 1 │ ┌────── │ ┌─────── │ ┌────── │ └────── │ └───────
|
||||||
|
│ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ │ 1 │ │ 1 │ │ 1 │ > 1 │ ┌───────
|
||||||
|
│ 3 │ 3 │ 3 │ > 3 │ > 3 │ 3 │ 3 │ > 3 │ │ 2 │ │ 2 │ │ 2 │ 2 │ │ > 1
|
||||||
|
│ 3/3 ─ │ 101 │ 3/3 ─ │ 101 │ 1/1 ─ │ ┌────── │ 3/3 ─ │ ┌────── │ └────── │ └─────── │ └────── │ 3 │ │ 2
|
||||||
|
│ > │ 102 │ > │ 102 │ > │ │ 101 │ > │ │ 101 │ > 3 │ > 3 │ > 3 │ 101 │ │ 3
|
||||||
|
└──────── │ 3/3 ─ │ 101 │ 1/1 ─ │ 101 │ │ 102 │ ┌────── │ │ 102 │ ┌────── │ ┌─────── │ ┌────── │ 102 │ └───────
|
||||||
|
│ > │ 102 │ > │ 102 │ └─HEAD─ │ │ 101 │ └─HEAD─ │ │ 101 │ │ 1/1 │ │ 101 │ ─────── │ ┌───────
|
||||||
|
└──────── └──────── └──────── └──────── │ 3/3 ─ │ │ 102 │ 1/1 ─ │ │ 102 │ │ > │ │ 102 │ 3/3 │ │ >
|
||||||
|
│ > │ └─HEAD─ │ > │ └─HEAD─ │ └─────── │ └─HEAD─ │ > │ └───────
|
||||||
|
└──────── └──────── └──────── │ 1/1 ─ │ ┌─────── └──────── └──────── │ ┌───────
|
||||||
|
│ > │ │ 101 │ │ 101
|
||||||
|
└──────── │ │ 102 │ │ 102
|
||||||
|
│ └─HEAD── │ └─HEAD──
|
||||||
|
└───────── └─────────
|
||||||
|
BLOCK
|
||||||
|
|
||||||
|
expects = []
|
||||||
|
output.each_line.first.scan(/\S+/) do
|
||||||
|
offset = Regexp.last_match.offset(0)
|
||||||
|
expects << output.lines.filter_map { it[offset[0]...offset[1]]&.strip }.take_while { !it.empty? }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
suffixes.zip(expects).each do |suffix, block|
|
||||||
|
tmux.send_keys(prefix + suffix, :Enter)
|
||||||
|
tmux.until { assert_block(block, it) }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { assert_block(block.downcase, it) }
|
||||||
|
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_change_header_and_label_at_once
|
def test_change_header_and_label_at_once
|
||||||
tmux.send_keys %(seq 10 | #{FZF} --border sharp --header-border sharp --header-label-pos 3 --bind 'focus:change-header(header)+change-header-label(label)'), :Enter
|
tmux.send_keys %(seq 10 | #{FZF} --border sharp --header-border sharp --header-label-pos 3 --bind 'focus:change-header(header)+change-header-label(label)'), :Enter
|
||||||
block = <<~BLOCK
|
block = <<~BLOCK
|
||||||
@@ -1033,4 +1153,79 @@ class TestLayout < TestInteractive
|
|||||||
BLOCK
|
BLOCK
|
||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_combinations
|
||||||
|
skip unless ENV['LONGTEST']
|
||||||
|
|
||||||
|
base = [
|
||||||
|
'--pointer=@',
|
||||||
|
'--exact',
|
||||||
|
'--query=123',
|
||||||
|
'--header="$(seq 101 103)"',
|
||||||
|
'--header-lines=3',
|
||||||
|
'--footer "$(seq 201 203)"',
|
||||||
|
'--preview "echo foobar"'
|
||||||
|
]
|
||||||
|
options = [
|
||||||
|
['--separator==', '--no-separator'],
|
||||||
|
['--info=default', '--info=inline', '--info=inline-right'],
|
||||||
|
['--no-input-border', '--input-border'],
|
||||||
|
['--no-header-border', '--header-border=none', '--header-border'],
|
||||||
|
['--no-header-lines-border', '--header-lines-border'],
|
||||||
|
['--no-footer-border', '--footer-border'],
|
||||||
|
['--no-list-border', '--list-border'],
|
||||||
|
['--preview-window=right', '--preview-window=up', '--preview-window=down', '--preview-window=left'],
|
||||||
|
['--header-first', '--no-header-first'],
|
||||||
|
['--layout=default', '--layout=reverse', '--layout=reverse-list']
|
||||||
|
]
|
||||||
|
# Combination of all options
|
||||||
|
combinations = options[0].product(*options.drop(1))
|
||||||
|
combinations.each_with_index do |combination, index|
|
||||||
|
opts = base + combination
|
||||||
|
command = %(seq 1001 2000 | #{FZF} #{opts.join(' ')})
|
||||||
|
puts "# #{index + 1}/#{combinations.length}\n#{command}"
|
||||||
|
tmux.send_keys command, :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
layout = combination.find { it.start_with?('--layout=') }.split('=').last
|
||||||
|
header_first = combination.include?('--header-first')
|
||||||
|
|
||||||
|
# Input
|
||||||
|
input = lines.index { it.include?('> 123') }
|
||||||
|
assert(input)
|
||||||
|
|
||||||
|
# Info
|
||||||
|
info = lines.index { it.include?('11/997') }
|
||||||
|
assert(info)
|
||||||
|
|
||||||
|
assert(layout == 'reverse' ? input <= info : input >= info)
|
||||||
|
|
||||||
|
# List
|
||||||
|
item1 = lines.index { it.include?('1230') }
|
||||||
|
item2 = lines.index { it.include?('1231') }
|
||||||
|
assert_equal(item1, layout == 'default' ? item2 + 1 : item2 - 1)
|
||||||
|
|
||||||
|
# Preview
|
||||||
|
assert(lines.any? { it.include?('foobar') })
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header1 = lines.index { it.include?('101') }
|
||||||
|
header2 = lines.index { it.include?('102') }
|
||||||
|
assert_equal(header2, header1 + 1)
|
||||||
|
assert((layout == 'reverse') == header_first ? input > header1 : input < header1)
|
||||||
|
|
||||||
|
# Footer
|
||||||
|
footer1 = lines.index { it.include?('201') }
|
||||||
|
footer2 = lines.index { it.include?('202') }
|
||||||
|
assert_equal(footer2, footer1 + 1)
|
||||||
|
assert(layout == 'reverse' ? footer1 > item2 : footer1 < item2)
|
||||||
|
|
||||||
|
# Header lines
|
||||||
|
hline1 = lines.index { it.include?('1001') }
|
||||||
|
hline2 = lines.index { it.include?('1002') }
|
||||||
|
assert_equal(hline1, layout == 'default' ? hline2 + 1 : hline2 - 1)
|
||||||
|
assert(layout == 'reverse' ? hline1 > header1 : hline1 < header1)
|
||||||
|
end
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user