mirror of
https://github.com/junegunn/fzf.git
synced 2026-02-27 12:02:33 +08:00
Add change-header-lines action to dynamically change --header-lines
All input lines now enter the chunklist with sequential indices, and header lines are excluded from matching via Pattern.startIndex and PassMerger offset. This allows the number of header lines to be changed at runtime with change-header-lines(N), transform-header-lines, and bg-transform-header-lines actions. - Remove EvtHeader event; header items are read directly from chunks - Add startIndex to Pattern and PassMerger for skipping header items - Add targetIndex field to Terminal for cursor repositioning across header-lines changes Close #4659
This commit is contained in:
@@ -1881,6 +1881,7 @@ A key or an event can be bound to one or more of the following actions.
|
||||
\fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string)
|
||||
\fBchange\-ghost(...)\fR (change ghost text to the given string)
|
||||
\fBchange\-header(...)\fR (change header to the given string; doesn't affect \fB\-\-header\-lines\fR)
|
||||
\fBchange\-header\-lines(N)\fR (change the number of \fB\-\-header\-lines\fR)
|
||||
\fBchange\-header\-label(...)\fR (change \fB\-\-header\-label\fR to the given string)
|
||||
\fBchange\-input\-label(...)\fR (change \fB\-\-input\-label\fR to the given string)
|
||||
\fBchange\-list\-label(...)\fR (change \fB\-\-list\-label\fR to the given string)
|
||||
@@ -1987,6 +1988,7 @@ A key or an event can be bound to one or more of the following actions.
|
||||
\fBtransform\-border\-label(...)\fR (transform border label using an external command)
|
||||
\fBtransform\-ghost(...)\fR (transform ghost text using an external command)
|
||||
\fBtransform\-header(...)\fR (transform header using an external command)
|
||||
\fBtransform\-header\-lines(...)\fR (transform the number of \fB\-\-header\-lines\fR using an external command)
|
||||
\fBtransform\-header\-label(...)\fR (transform header label using an external command)
|
||||
\fBtransform\-input\-label(...)\fR (transform input label using an external command)
|
||||
\fBtransform\-list\-label(...)\fR (transform list label using an external command)
|
||||
|
||||
@@ -30,161 +30,164 @@ func _() {
|
||||
_ = x[actChangeBorderLabel-19]
|
||||
_ = x[actChangeGhost-20]
|
||||
_ = x[actChangeHeader-21]
|
||||
_ = x[actChangeFooter-22]
|
||||
_ = x[actChangeHeaderLabel-23]
|
||||
_ = x[actChangeFooterLabel-24]
|
||||
_ = x[actChangeInputLabel-25]
|
||||
_ = x[actChangeListLabel-26]
|
||||
_ = x[actChangeMulti-27]
|
||||
_ = x[actChangeNth-28]
|
||||
_ = x[actChangePointer-29]
|
||||
_ = x[actChangePreview-30]
|
||||
_ = x[actChangePreviewLabel-31]
|
||||
_ = x[actChangePreviewWindow-32]
|
||||
_ = x[actChangePrompt-33]
|
||||
_ = x[actChangeQuery-34]
|
||||
_ = x[actClearScreen-35]
|
||||
_ = x[actClearQuery-36]
|
||||
_ = x[actClearSelection-37]
|
||||
_ = x[actClose-38]
|
||||
_ = x[actDeleteChar-39]
|
||||
_ = x[actDeleteCharEof-40]
|
||||
_ = x[actEndOfLine-41]
|
||||
_ = x[actFatal-42]
|
||||
_ = x[actForwardChar-43]
|
||||
_ = x[actForwardWord-44]
|
||||
_ = x[actForwardSubWord-45]
|
||||
_ = x[actKillLine-46]
|
||||
_ = x[actKillWord-47]
|
||||
_ = x[actKillSubWord-48]
|
||||
_ = x[actUnixLineDiscard-49]
|
||||
_ = x[actUnixWordRubout-50]
|
||||
_ = x[actYank-51]
|
||||
_ = x[actBackwardKillWord-52]
|
||||
_ = x[actBackwardKillSubWord-53]
|
||||
_ = x[actSelectAll-54]
|
||||
_ = x[actDeselectAll-55]
|
||||
_ = x[actToggle-56]
|
||||
_ = x[actToggleSearch-57]
|
||||
_ = x[actToggleAll-58]
|
||||
_ = x[actToggleDown-59]
|
||||
_ = x[actToggleUp-60]
|
||||
_ = x[actToggleIn-61]
|
||||
_ = x[actToggleOut-62]
|
||||
_ = x[actToggleTrack-63]
|
||||
_ = x[actToggleTrackCurrent-64]
|
||||
_ = x[actToggleHeader-65]
|
||||
_ = x[actToggleWrap-66]
|
||||
_ = x[actToggleWrapWord-67]
|
||||
_ = x[actToggleMultiLine-68]
|
||||
_ = x[actToggleHscroll-69]
|
||||
_ = x[actToggleRaw-70]
|
||||
_ = x[actEnableRaw-71]
|
||||
_ = x[actDisableRaw-72]
|
||||
_ = x[actTrackCurrent-73]
|
||||
_ = x[actToggleInput-74]
|
||||
_ = x[actHideInput-75]
|
||||
_ = x[actShowInput-76]
|
||||
_ = x[actUntrackCurrent-77]
|
||||
_ = x[actDown-78]
|
||||
_ = x[actDownMatch-79]
|
||||
_ = x[actUp-80]
|
||||
_ = x[actUpMatch-81]
|
||||
_ = x[actPageUp-82]
|
||||
_ = x[actPageDown-83]
|
||||
_ = x[actPosition-84]
|
||||
_ = x[actHalfPageUp-85]
|
||||
_ = x[actHalfPageDown-86]
|
||||
_ = x[actOffsetUp-87]
|
||||
_ = x[actOffsetDown-88]
|
||||
_ = x[actOffsetMiddle-89]
|
||||
_ = x[actJump-90]
|
||||
_ = x[actJumpAccept-91]
|
||||
_ = x[actPrintQuery-92]
|
||||
_ = x[actRefreshPreview-93]
|
||||
_ = x[actReplaceQuery-94]
|
||||
_ = x[actToggleSort-95]
|
||||
_ = x[actShowPreview-96]
|
||||
_ = x[actHidePreview-97]
|
||||
_ = x[actTogglePreview-98]
|
||||
_ = x[actTogglePreviewWrap-99]
|
||||
_ = x[actTogglePreviewWrapWord-100]
|
||||
_ = x[actTransform-101]
|
||||
_ = x[actTransformBorderLabel-102]
|
||||
_ = x[actTransformGhost-103]
|
||||
_ = x[actTransformHeader-104]
|
||||
_ = x[actTransformFooter-105]
|
||||
_ = x[actTransformHeaderLabel-106]
|
||||
_ = x[actTransformFooterLabel-107]
|
||||
_ = x[actTransformInputLabel-108]
|
||||
_ = x[actTransformListLabel-109]
|
||||
_ = x[actTransformNth-110]
|
||||
_ = x[actTransformPointer-111]
|
||||
_ = x[actTransformPreviewLabel-112]
|
||||
_ = x[actTransformPrompt-113]
|
||||
_ = x[actTransformQuery-114]
|
||||
_ = x[actTransformSearch-115]
|
||||
_ = x[actTrigger-116]
|
||||
_ = x[actBgTransform-117]
|
||||
_ = x[actBgTransformBorderLabel-118]
|
||||
_ = x[actBgTransformGhost-119]
|
||||
_ = x[actBgTransformHeader-120]
|
||||
_ = x[actBgTransformFooter-121]
|
||||
_ = x[actBgTransformHeaderLabel-122]
|
||||
_ = x[actBgTransformFooterLabel-123]
|
||||
_ = x[actBgTransformInputLabel-124]
|
||||
_ = x[actBgTransformListLabel-125]
|
||||
_ = x[actBgTransformNth-126]
|
||||
_ = x[actBgTransformPointer-127]
|
||||
_ = x[actBgTransformPreviewLabel-128]
|
||||
_ = x[actBgTransformPrompt-129]
|
||||
_ = x[actBgTransformQuery-130]
|
||||
_ = x[actBgTransformSearch-131]
|
||||
_ = x[actBgCancel-132]
|
||||
_ = x[actSearch-133]
|
||||
_ = x[actPreview-134]
|
||||
_ = x[actPreviewTop-135]
|
||||
_ = x[actPreviewBottom-136]
|
||||
_ = x[actPreviewUp-137]
|
||||
_ = x[actPreviewDown-138]
|
||||
_ = x[actPreviewPageUp-139]
|
||||
_ = x[actPreviewPageDown-140]
|
||||
_ = x[actPreviewHalfPageUp-141]
|
||||
_ = x[actPreviewHalfPageDown-142]
|
||||
_ = x[actPrevHistory-143]
|
||||
_ = x[actPrevSelected-144]
|
||||
_ = x[actPrint-145]
|
||||
_ = x[actPut-146]
|
||||
_ = x[actNextHistory-147]
|
||||
_ = x[actNextSelected-148]
|
||||
_ = x[actExecute-149]
|
||||
_ = x[actExecuteSilent-150]
|
||||
_ = x[actExecuteMulti-151]
|
||||
_ = x[actSigStop-152]
|
||||
_ = x[actBest-153]
|
||||
_ = x[actFirst-154]
|
||||
_ = x[actLast-155]
|
||||
_ = x[actReload-156]
|
||||
_ = x[actReloadSync-157]
|
||||
_ = x[actDisableSearch-158]
|
||||
_ = x[actEnableSearch-159]
|
||||
_ = x[actSelect-160]
|
||||
_ = x[actDeselect-161]
|
||||
_ = x[actUnbind-162]
|
||||
_ = x[actRebind-163]
|
||||
_ = x[actToggleBind-164]
|
||||
_ = x[actBecome-165]
|
||||
_ = x[actShowHeader-166]
|
||||
_ = x[actHideHeader-167]
|
||||
_ = x[actBell-168]
|
||||
_ = x[actExclude-169]
|
||||
_ = x[actExcludeMulti-170]
|
||||
_ = x[actAsync-171]
|
||||
_ = x[actChangeHeaderLines-22]
|
||||
_ = x[actChangeFooter-23]
|
||||
_ = x[actChangeHeaderLabel-24]
|
||||
_ = x[actChangeFooterLabel-25]
|
||||
_ = x[actChangeInputLabel-26]
|
||||
_ = x[actChangeListLabel-27]
|
||||
_ = x[actChangeMulti-28]
|
||||
_ = x[actChangeNth-29]
|
||||
_ = x[actChangePointer-30]
|
||||
_ = x[actChangePreview-31]
|
||||
_ = x[actChangePreviewLabel-32]
|
||||
_ = x[actChangePreviewWindow-33]
|
||||
_ = x[actChangePrompt-34]
|
||||
_ = x[actChangeQuery-35]
|
||||
_ = x[actClearScreen-36]
|
||||
_ = x[actClearQuery-37]
|
||||
_ = x[actClearSelection-38]
|
||||
_ = x[actClose-39]
|
||||
_ = x[actDeleteChar-40]
|
||||
_ = x[actDeleteCharEof-41]
|
||||
_ = x[actEndOfLine-42]
|
||||
_ = x[actFatal-43]
|
||||
_ = x[actForwardChar-44]
|
||||
_ = x[actForwardWord-45]
|
||||
_ = x[actForwardSubWord-46]
|
||||
_ = x[actKillLine-47]
|
||||
_ = x[actKillWord-48]
|
||||
_ = x[actKillSubWord-49]
|
||||
_ = x[actUnixLineDiscard-50]
|
||||
_ = x[actUnixWordRubout-51]
|
||||
_ = x[actYank-52]
|
||||
_ = x[actBackwardKillWord-53]
|
||||
_ = x[actBackwardKillSubWord-54]
|
||||
_ = x[actSelectAll-55]
|
||||
_ = x[actDeselectAll-56]
|
||||
_ = x[actToggle-57]
|
||||
_ = x[actToggleSearch-58]
|
||||
_ = x[actToggleAll-59]
|
||||
_ = x[actToggleDown-60]
|
||||
_ = x[actToggleUp-61]
|
||||
_ = x[actToggleIn-62]
|
||||
_ = x[actToggleOut-63]
|
||||
_ = x[actToggleTrack-64]
|
||||
_ = x[actToggleTrackCurrent-65]
|
||||
_ = x[actToggleHeader-66]
|
||||
_ = x[actToggleWrap-67]
|
||||
_ = x[actToggleWrapWord-68]
|
||||
_ = x[actToggleMultiLine-69]
|
||||
_ = x[actToggleHscroll-70]
|
||||
_ = x[actToggleRaw-71]
|
||||
_ = x[actEnableRaw-72]
|
||||
_ = x[actDisableRaw-73]
|
||||
_ = x[actTrackCurrent-74]
|
||||
_ = x[actToggleInput-75]
|
||||
_ = x[actHideInput-76]
|
||||
_ = x[actShowInput-77]
|
||||
_ = x[actUntrackCurrent-78]
|
||||
_ = x[actDown-79]
|
||||
_ = x[actDownMatch-80]
|
||||
_ = x[actUp-81]
|
||||
_ = x[actUpMatch-82]
|
||||
_ = x[actPageUp-83]
|
||||
_ = x[actPageDown-84]
|
||||
_ = x[actPosition-85]
|
||||
_ = x[actHalfPageUp-86]
|
||||
_ = x[actHalfPageDown-87]
|
||||
_ = x[actOffsetUp-88]
|
||||
_ = x[actOffsetDown-89]
|
||||
_ = x[actOffsetMiddle-90]
|
||||
_ = x[actJump-91]
|
||||
_ = x[actJumpAccept-92]
|
||||
_ = x[actPrintQuery-93]
|
||||
_ = x[actRefreshPreview-94]
|
||||
_ = x[actReplaceQuery-95]
|
||||
_ = x[actToggleSort-96]
|
||||
_ = x[actShowPreview-97]
|
||||
_ = x[actHidePreview-98]
|
||||
_ = x[actTogglePreview-99]
|
||||
_ = x[actTogglePreviewWrap-100]
|
||||
_ = x[actTogglePreviewWrapWord-101]
|
||||
_ = x[actTransform-102]
|
||||
_ = x[actTransformBorderLabel-103]
|
||||
_ = x[actTransformGhost-104]
|
||||
_ = x[actTransformHeader-105]
|
||||
_ = x[actTransformHeaderLines-106]
|
||||
_ = x[actTransformFooter-107]
|
||||
_ = x[actTransformHeaderLabel-108]
|
||||
_ = x[actTransformFooterLabel-109]
|
||||
_ = x[actTransformInputLabel-110]
|
||||
_ = x[actTransformListLabel-111]
|
||||
_ = x[actTransformNth-112]
|
||||
_ = x[actTransformPointer-113]
|
||||
_ = x[actTransformPreviewLabel-114]
|
||||
_ = x[actTransformPrompt-115]
|
||||
_ = x[actTransformQuery-116]
|
||||
_ = x[actTransformSearch-117]
|
||||
_ = x[actTrigger-118]
|
||||
_ = x[actBgTransform-119]
|
||||
_ = x[actBgTransformBorderLabel-120]
|
||||
_ = x[actBgTransformGhost-121]
|
||||
_ = x[actBgTransformHeader-122]
|
||||
_ = x[actBgTransformHeaderLines-123]
|
||||
_ = x[actBgTransformFooter-124]
|
||||
_ = x[actBgTransformHeaderLabel-125]
|
||||
_ = x[actBgTransformFooterLabel-126]
|
||||
_ = x[actBgTransformInputLabel-127]
|
||||
_ = x[actBgTransformListLabel-128]
|
||||
_ = x[actBgTransformNth-129]
|
||||
_ = x[actBgTransformPointer-130]
|
||||
_ = x[actBgTransformPreviewLabel-131]
|
||||
_ = x[actBgTransformPrompt-132]
|
||||
_ = x[actBgTransformQuery-133]
|
||||
_ = x[actBgTransformSearch-134]
|
||||
_ = x[actBgCancel-135]
|
||||
_ = x[actSearch-136]
|
||||
_ = x[actPreview-137]
|
||||
_ = x[actPreviewTop-138]
|
||||
_ = x[actPreviewBottom-139]
|
||||
_ = x[actPreviewUp-140]
|
||||
_ = x[actPreviewDown-141]
|
||||
_ = x[actPreviewPageUp-142]
|
||||
_ = x[actPreviewPageDown-143]
|
||||
_ = x[actPreviewHalfPageUp-144]
|
||||
_ = x[actPreviewHalfPageDown-145]
|
||||
_ = x[actPrevHistory-146]
|
||||
_ = x[actPrevSelected-147]
|
||||
_ = x[actPrint-148]
|
||||
_ = x[actPut-149]
|
||||
_ = x[actNextHistory-150]
|
||||
_ = x[actNextSelected-151]
|
||||
_ = x[actExecute-152]
|
||||
_ = x[actExecuteSilent-153]
|
||||
_ = x[actExecuteMulti-154]
|
||||
_ = x[actSigStop-155]
|
||||
_ = x[actBest-156]
|
||||
_ = x[actFirst-157]
|
||||
_ = x[actLast-158]
|
||||
_ = x[actReload-159]
|
||||
_ = x[actReloadSync-160]
|
||||
_ = x[actDisableSearch-161]
|
||||
_ = x[actEnableSearch-162]
|
||||
_ = x[actSelect-163]
|
||||
_ = x[actDeselect-164]
|
||||
_ = x[actUnbind-165]
|
||||
_ = x[actRebind-166]
|
||||
_ = x[actToggleBind-167]
|
||||
_ = x[actBecome-168]
|
||||
_ = x[actShowHeader-169]
|
||||
_ = x[actHideHeader-170]
|
||||
_ = x[actBell-171]
|
||||
_ = x[actExclude-172]
|
||||
_ = x[actExcludeMulti-173]
|
||||
_ = x[actAsync-174]
|
||||
}
|
||||
|
||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleWrapWordactToggleMultiLineactToggleHscrollactToggleRawactEnableRawactDisableRawactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactDownMatchactUpactUpMatchactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTogglePreviewWrapWordactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactBestactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeHeaderLinesactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleWrapWordactToggleMultiLineactToggleHscrollactToggleRawactEnableRawactDisableRawactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactDownMatchactUpactUpMatchactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTogglePreviewWrapWordactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformHeaderLinesactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformHeaderLinesactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactBestactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
||||
|
||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 331, 351, 371, 390, 408, 422, 434, 450, 466, 487, 509, 524, 538, 552, 565, 582, 590, 603, 619, 631, 639, 653, 667, 684, 695, 706, 720, 738, 755, 762, 781, 803, 815, 829, 838, 853, 865, 878, 889, 900, 912, 926, 947, 962, 975, 992, 1010, 1026, 1038, 1050, 1063, 1078, 1092, 1104, 1116, 1133, 1140, 1152, 1157, 1167, 1176, 1187, 1198, 1211, 1226, 1237, 1250, 1265, 1272, 1285, 1298, 1315, 1330, 1343, 1357, 1371, 1387, 1407, 1431, 1443, 1466, 1483, 1501, 1519, 1542, 1565, 1587, 1608, 1623, 1642, 1666, 1684, 1701, 1719, 1729, 1743, 1768, 1787, 1807, 1827, 1852, 1877, 1901, 1924, 1941, 1962, 1988, 2008, 2027, 2047, 2058, 2067, 2077, 2090, 2106, 2118, 2132, 2148, 2166, 2186, 2208, 2222, 2237, 2245, 2251, 2265, 2280, 2290, 2306, 2321, 2331, 2338, 2346, 2353, 2362, 2375, 2391, 2406, 2415, 2426, 2435, 2444, 2457, 2466, 2479, 2492, 2499, 2509, 2524, 2532}
|
||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 336, 351, 371, 391, 410, 428, 442, 454, 470, 486, 507, 529, 544, 558, 572, 585, 602, 610, 623, 639, 651, 659, 673, 687, 704, 715, 726, 740, 758, 775, 782, 801, 823, 835, 849, 858, 873, 885, 898, 909, 920, 932, 946, 967, 982, 995, 1012, 1030, 1046, 1058, 1070, 1083, 1098, 1112, 1124, 1136, 1153, 1160, 1172, 1177, 1187, 1196, 1207, 1218, 1231, 1246, 1257, 1270, 1285, 1292, 1305, 1318, 1335, 1350, 1363, 1377, 1391, 1407, 1427, 1451, 1463, 1486, 1503, 1521, 1544, 1562, 1585, 1608, 1630, 1651, 1666, 1685, 1709, 1727, 1744, 1762, 1772, 1786, 1811, 1830, 1850, 1875, 1895, 1920, 1945, 1969, 1992, 2009, 2030, 2056, 2076, 2095, 2115, 2126, 2135, 2145, 2158, 2174, 2186, 2200, 2216, 2234, 2254, 2276, 2290, 2305, 2313, 2319, 2333, 2348, 2358, 2374, 2389, 2399, 2406, 2414, 2421, 2430, 2443, 2459, 2474, 2483, 2494, 2503, 2512, 2525, 2534, 2547, 2560, 2567, 2577, 2592, 2600}
|
||||
|
||||
func (i actionType) String() string {
|
||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||
|
||||
@@ -52,6 +52,20 @@ func (cl *ChunkList) lastChunk() *Chunk {
|
||||
return cl.chunks[len(cl.chunks)-1]
|
||||
}
|
||||
|
||||
// GetItems returns the first n items from the given chunks
|
||||
func GetItems(chunks []*Chunk, n int) []Item {
|
||||
items := make([]Item, 0, n)
|
||||
for _, chunk := range chunks {
|
||||
for i := 0; i < chunk.count && len(items) < n; i++ {
|
||||
items = append(items, chunk.items[i])
|
||||
}
|
||||
if len(items) >= n {
|
||||
break
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// CountItems returns the total number of Items
|
||||
func CountItems(cs []*Chunk) int {
|
||||
if len(cs) == 0 {
|
||||
|
||||
@@ -65,7 +65,6 @@ const (
|
||||
EvtSearchNew
|
||||
EvtSearchProgress
|
||||
EvtSearchFin
|
||||
EvtHeader
|
||||
EvtReady
|
||||
EvtQuit
|
||||
)
|
||||
|
||||
47
src/core.go
47
src/core.go
@@ -17,7 +17,6 @@ Reader -> EvtReadNew -> Matcher (restart)
|
||||
Terminal -> EvtSearchNew:bool -> Matcher (restart)
|
||||
Matcher -> EvtSearchProgress -> Terminal (update info)
|
||||
Matcher -> EvtSearchFin -> Terminal (update list)
|
||||
Matcher -> EvtHeader -> Terminal (update header)
|
||||
*/
|
||||
|
||||
type revision struct {
|
||||
@@ -113,14 +112,8 @@ func Run(opts *Options) (int, error) {
|
||||
cache := NewChunkCache()
|
||||
var chunkList *ChunkList
|
||||
var itemIndex int32
|
||||
header := make([]string, 0, opts.HeaderLines)
|
||||
if opts.WithNth == nil {
|
||||
chunkList = NewChunkList(cache, func(item *Item, data []byte) bool {
|
||||
if len(header) < opts.HeaderLines {
|
||||
header = append(header, byteString(data))
|
||||
eventBox.Set(EvtHeader, header)
|
||||
return false
|
||||
}
|
||||
item.text, item.colors = ansiProcessor(data)
|
||||
item.text.Index = itemIndex
|
||||
itemIndex++
|
||||
@@ -147,11 +140,6 @@ func Run(opts *Options) (int, error) {
|
||||
}
|
||||
}
|
||||
transformed := nthTransformer(tokens, itemIndex)
|
||||
if len(header) < opts.HeaderLines {
|
||||
header = append(header, transformed)
|
||||
eventBox.Set(EvtHeader, header)
|
||||
return false
|
||||
}
|
||||
item.text, item.colors = ansiProcessor(stringBytes(transformed))
|
||||
|
||||
// We should not trim trailing whitespaces with background colors
|
||||
@@ -236,13 +224,15 @@ func Run(opts *Options) (int, error) {
|
||||
denylist = make(map[int32]struct{})
|
||||
denyMutex.Unlock()
|
||||
}
|
||||
headerLines := int32(opts.HeaderLines)
|
||||
headerUpdated := false
|
||||
patternBuilder := func(runes []rune) *Pattern {
|
||||
denyMutex.Lock()
|
||||
denylistCopy := maps.Clone(denylist)
|
||||
denyMutex.Unlock()
|
||||
return BuildPattern(cache, patternCache,
|
||||
opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
|
||||
opts.Filter == nil, nth, opts.Delimiter, inputRevision, runes, denylistCopy)
|
||||
opts.Filter == nil, nth, opts.Delimiter, inputRevision, runes, denylistCopy, headerLines)
|
||||
}
|
||||
matcher := NewMatcher(cache, patternBuilder, sort, opts.Tac, eventBox, inputRevision)
|
||||
|
||||
@@ -265,6 +255,9 @@ func Run(opts *Options) (int, error) {
|
||||
func(runes []byte) bool {
|
||||
item := Item{}
|
||||
if chunkList.trans(&item, runes) {
|
||||
if item.Index() < headerLines {
|
||||
return false
|
||||
}
|
||||
mutex.Lock()
|
||||
if result, _, _ := pattern.MatchItem(&item, false, slab); result != nil {
|
||||
opts.Printer(transformer(&item))
|
||||
@@ -349,11 +342,11 @@ func Run(opts *Options) (int, error) {
|
||||
clearDenylist()
|
||||
}
|
||||
reading = true
|
||||
headerUpdated = false
|
||||
startTick = ticks
|
||||
chunkList.Clear()
|
||||
itemIndex = 0
|
||||
inputRevision.bumpMajor()
|
||||
header = make([]string, 0, opts.HeaderLines)
|
||||
readyChan := make(chan bool)
|
||||
go reader.restart(command, environ, readyChan)
|
||||
<-readyChan
|
||||
@@ -411,7 +404,11 @@ func Run(opts *Options) (int, error) {
|
||||
snapshotRevision = inputRevision
|
||||
}
|
||||
total = count
|
||||
terminal.UpdateCount(total, !reading, value.(*string))
|
||||
terminal.UpdateCount(max(0, total-int(headerLines)), !reading, value.(*string))
|
||||
if headerLines > 0 && !headerUpdated {
|
||||
terminal.UpdateHeader(GetItems(snapshot, int(headerLines)))
|
||||
headerUpdated = int32(total) >= headerLines
|
||||
}
|
||||
if heightUnknown && !deferred {
|
||||
determine(!reading)
|
||||
}
|
||||
@@ -421,6 +418,7 @@ func Run(opts *Options) (int, error) {
|
||||
var command *commandSpec
|
||||
var environ []string
|
||||
var changed bool
|
||||
headerLinesChanged := false
|
||||
switch val := value.(type) {
|
||||
case searchRequest:
|
||||
sort = val.sort
|
||||
@@ -441,6 +439,12 @@ func Run(opts *Options) (int, error) {
|
||||
nth = *val.nth
|
||||
bump = true
|
||||
}
|
||||
if val.headerLines != nil {
|
||||
headerLines = int32(*val.headerLines)
|
||||
headerUpdated = false
|
||||
headerLinesChanged = true
|
||||
bump = true
|
||||
}
|
||||
if bump {
|
||||
patternCache = make(map[string]*Pattern)
|
||||
cache.Clear()
|
||||
@@ -477,6 +481,14 @@ func Run(opts *Options) (int, error) {
|
||||
snapshotRevision = inputRevision
|
||||
}
|
||||
}
|
||||
if headerLinesChanged {
|
||||
terminal.UpdateCount(max(0, total-int(headerLines)), !reading, nil)
|
||||
if headerLines > 0 {
|
||||
terminal.UpdateHeader(GetItems(snapshot, int(headerLines)))
|
||||
} else {
|
||||
terminal.UpdateHeader(nil)
|
||||
}
|
||||
}
|
||||
matcher.Reset(snapshot, input(), true, !reading, sort, snapshotRevision)
|
||||
delay = false
|
||||
|
||||
@@ -486,11 +498,6 @@ func Run(opts *Options) (int, error) {
|
||||
terminal.UpdateProgress(val)
|
||||
}
|
||||
|
||||
case EvtHeader:
|
||||
headerPadded := make([]string, opts.HeaderLines)
|
||||
copy(headerPadded, value.([]string))
|
||||
terminal.UpdateHeader(headerPadded)
|
||||
|
||||
case EvtSearchFin:
|
||||
switch val := value.(type) {
|
||||
case MatchResult:
|
||||
|
||||
@@ -174,7 +174,7 @@ func (m *Matcher) scan(request MatchRequest) MatchResult {
|
||||
return MatchResult{m, m, false}
|
||||
}
|
||||
pattern := request.pattern
|
||||
passMerger := PassMerger(&request.chunks, m.tac, request.revision)
|
||||
passMerger := PassMerger(&request.chunks, m.tac, request.revision, pattern.startIndex)
|
||||
if pattern.IsEmpty() {
|
||||
return MatchResult{passMerger, passMerger, false}
|
||||
}
|
||||
|
||||
@@ -10,42 +10,46 @@ func EmptyMerger(revision revision) *Merger {
|
||||
// Merger holds a set of locally sorted lists of items and provides the view of
|
||||
// a single, globally-sorted list
|
||||
type Merger struct {
|
||||
pattern *Pattern
|
||||
lists [][]Result
|
||||
merged []Result
|
||||
chunks *[]*Chunk
|
||||
cursors []int
|
||||
sorted bool
|
||||
tac bool
|
||||
final bool
|
||||
count int
|
||||
pass bool
|
||||
revision revision
|
||||
minIndex int32
|
||||
maxIndex int32
|
||||
pattern *Pattern
|
||||
lists [][]Result
|
||||
merged []Result
|
||||
chunks *[]*Chunk
|
||||
cursors []int
|
||||
sorted bool
|
||||
tac bool
|
||||
final bool
|
||||
count int
|
||||
pass bool
|
||||
startIndex int
|
||||
revision revision
|
||||
minIndex int32
|
||||
maxIndex int32
|
||||
}
|
||||
|
||||
// PassMerger returns a new Merger that simply returns the items in the
|
||||
// original order
|
||||
func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger {
|
||||
// original order. startIndex items are skipped from the beginning.
|
||||
func PassMerger(chunks *[]*Chunk, tac bool, revision revision, startIndex int32) *Merger {
|
||||
var minIndex, maxIndex int32
|
||||
if len(*chunks) > 0 {
|
||||
minIndex = (*chunks)[0].items[0].Index()
|
||||
maxIndex = (*chunks)[len(*chunks)-1].lastIndex(minIndex)
|
||||
}
|
||||
si := int(startIndex)
|
||||
mg := Merger{
|
||||
pattern: nil,
|
||||
chunks: chunks,
|
||||
tac: tac,
|
||||
count: 0,
|
||||
pass: true,
|
||||
revision: revision,
|
||||
minIndex: minIndex,
|
||||
maxIndex: maxIndex}
|
||||
pattern: nil,
|
||||
chunks: chunks,
|
||||
tac: tac,
|
||||
count: 0,
|
||||
pass: true,
|
||||
startIndex: si,
|
||||
revision: revision,
|
||||
minIndex: minIndex + startIndex,
|
||||
maxIndex: maxIndex}
|
||||
|
||||
for _, chunk := range *mg.chunks {
|
||||
mg.count += chunk.count
|
||||
}
|
||||
mg.count = max(0, mg.count-si)
|
||||
return &mg
|
||||
}
|
||||
|
||||
@@ -113,6 +117,7 @@ func (mg *Merger) Get(idx int) Result {
|
||||
if mg.tac {
|
||||
idx = mg.count - idx - 1
|
||||
}
|
||||
idx += mg.startIndex
|
||||
firstChunk := (*mg.chunks)[0]
|
||||
if firstChunk.count < chunkSize && idx >= firstChunk.count {
|
||||
idx -= firstChunk.count
|
||||
|
||||
@@ -1626,7 +1626,7 @@ const (
|
||||
|
||||
func init() {
|
||||
executeRegexp = regexp.MustCompile(
|
||||
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|bg-transform|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|bg-transform|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search|trigger)`)
|
||||
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|bg-transform|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header-lines|header|footer|search|nth|pointer|ghost)|bg-transform|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search|trigger)`)
|
||||
splitRegexp = regexp.MustCompile("[,:]+")
|
||||
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
||||
}
|
||||
@@ -2037,6 +2037,8 @@ func isExecuteAction(str string) actionType {
|
||||
return actPreview
|
||||
case "change-header":
|
||||
return actChangeHeader
|
||||
case "change-header-lines":
|
||||
return actChangeHeaderLines
|
||||
case "change-footer":
|
||||
return actChangeFooter
|
||||
case "change-list-label":
|
||||
@@ -2097,6 +2099,8 @@ func isExecuteAction(str string) actionType {
|
||||
return actTransformFooter
|
||||
case "transform-header":
|
||||
return actTransformHeader
|
||||
case "transform-header-lines":
|
||||
return actTransformHeaderLines
|
||||
case "transform-ghost":
|
||||
return actTransformGhost
|
||||
case "transform-nth":
|
||||
@@ -2127,6 +2131,8 @@ func isExecuteAction(str string) actionType {
|
||||
return actBgTransformFooter
|
||||
case "bg-transform-header":
|
||||
return actBgTransformHeader
|
||||
case "bg-transform-header-lines":
|
||||
return actBgTransformHeaderLines
|
||||
case "bg-transform-ghost":
|
||||
return actBgTransformGhost
|
||||
case "bg-transform-nth":
|
||||
|
||||
@@ -64,6 +64,7 @@ type Pattern struct {
|
||||
procFun map[termType]algo.Algo
|
||||
cache *ChunkCache
|
||||
denylist map[int32]struct{}
|
||||
startIndex int32
|
||||
}
|
||||
|
||||
var _splitRegex *regexp.Regexp
|
||||
@@ -74,7 +75,7 @@ func init() {
|
||||
|
||||
// BuildPattern builds Pattern object from the given arguments
|
||||
func BuildPattern(cache *ChunkCache, patternCache map[string]*Pattern, fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case, normalize bool, forward bool,
|
||||
withPos bool, cacheable bool, nth []Range, delimiter Delimiter, revision revision, runes []rune, denylist map[int32]struct{}) *Pattern {
|
||||
withPos bool, cacheable bool, nth []Range, delimiter Delimiter, revision revision, runes []rune, denylist map[int32]struct{}, startIndex int32) *Pattern {
|
||||
|
||||
var asString string
|
||||
if extended {
|
||||
@@ -146,6 +147,7 @@ func BuildPattern(cache *ChunkCache, patternCache map[string]*Pattern, fuzzy boo
|
||||
delimiter: delimiter,
|
||||
cache: cache,
|
||||
denylist: denylist,
|
||||
startIndex: startIndex,
|
||||
procFun: make(map[termType]algo.Algo)}
|
||||
|
||||
ptr.cacheKey = ptr.buildCacheKey()
|
||||
@@ -301,10 +303,19 @@ func (p *Pattern) Match(chunk *Chunk, slab *util.Slab) []Result {
|
||||
func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Result {
|
||||
matches := []Result{}
|
||||
|
||||
// Skip header items in chunks that contain them
|
||||
startIdx := 0
|
||||
if p.startIndex > 0 && chunk.count > 0 && chunk.items[0].Index() < p.startIndex {
|
||||
startIdx = int(p.startIndex - chunk.items[0].Index())
|
||||
if startIdx >= chunk.count {
|
||||
return matches
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.denylist) == 0 {
|
||||
// Huge code duplication for minimizing unnecessary map lookups
|
||||
if space == nil {
|
||||
for idx := 0; idx < chunk.count; idx++ {
|
||||
for idx := startIdx; idx < chunk.count; idx++ {
|
||||
if match, _, _ := p.MatchItem(&chunk.items[idx], p.withPos, slab); match != nil {
|
||||
matches = append(matches, *match)
|
||||
}
|
||||
@@ -320,7 +331,7 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Re
|
||||
}
|
||||
|
||||
if space == nil {
|
||||
for idx := 0; idx < chunk.count; idx++ {
|
||||
for idx := startIdx; idx < chunk.count; idx++ {
|
||||
if _, prs := p.denylist[chunk.items[idx].Index()]; prs {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func buildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
|
||||
withPos bool, cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern {
|
||||
return BuildPattern(NewChunkCache(), make(map[string]*Pattern),
|
||||
fuzzy, fuzzyAlgo, extended, caseMode, normalize, forward,
|
||||
withPos, cacheable, nth, delimiter, revision{}, runes, nil)
|
||||
withPos, cacheable, nth, delimiter, revision{}, runes, nil, 0)
|
||||
}
|
||||
|
||||
func TestExact(t *testing.T) {
|
||||
|
||||
@@ -314,6 +314,7 @@ type Terminal struct {
|
||||
sort bool
|
||||
toggleSort bool
|
||||
track trackOption
|
||||
targetIndex int32
|
||||
delimiter Delimiter
|
||||
expect map[tui.Event]string
|
||||
keymap map[tui.Event][]*action
|
||||
@@ -327,7 +328,7 @@ type Terminal struct {
|
||||
headerVisible bool
|
||||
headerFirst bool
|
||||
headerLines int
|
||||
header []string
|
||||
header []Item
|
||||
header0 []string
|
||||
footer []string
|
||||
ellipsis string
|
||||
@@ -542,6 +543,7 @@ const (
|
||||
actChangeBorderLabel
|
||||
actChangeGhost
|
||||
actChangeHeader
|
||||
actChangeHeaderLines
|
||||
actChangeFooter
|
||||
actChangeHeaderLabel
|
||||
actChangeFooterLabel
|
||||
@@ -627,6 +629,7 @@ const (
|
||||
actTransformBorderLabel
|
||||
actTransformGhost
|
||||
actTransformHeader
|
||||
actTransformHeaderLines
|
||||
actTransformFooter
|
||||
actTransformHeaderLabel
|
||||
actTransformFooterLabel
|
||||
@@ -645,6 +648,7 @@ const (
|
||||
actBgTransformBorderLabel
|
||||
actBgTransformGhost
|
||||
actBgTransformHeader
|
||||
actBgTransformHeaderLines
|
||||
actBgTransformFooter
|
||||
actBgTransformHeaderLabel
|
||||
actBgTransformFooterLabel
|
||||
@@ -710,6 +714,7 @@ func processExecution(action actionType) bool {
|
||||
actTransformBorderLabel,
|
||||
actTransformGhost,
|
||||
actTransformHeader,
|
||||
actTransformHeaderLines,
|
||||
actTransformFooter,
|
||||
actTransformHeaderLabel,
|
||||
actTransformFooterLabel,
|
||||
@@ -725,6 +730,7 @@ func processExecution(action actionType) bool {
|
||||
actBgTransformBorderLabel,
|
||||
actBgTransformGhost,
|
||||
actBgTransformHeader,
|
||||
actBgTransformHeaderLines,
|
||||
actBgTransformFooter,
|
||||
actBgTransformHeaderLabel,
|
||||
actBgTransformFooterLabel,
|
||||
@@ -761,14 +767,15 @@ type placeholderFlags struct {
|
||||
}
|
||||
|
||||
type searchRequest struct {
|
||||
sort bool
|
||||
sync bool
|
||||
nth *[]Range
|
||||
command *commandSpec
|
||||
environ []string
|
||||
changed bool
|
||||
denylist []int32
|
||||
revision revision
|
||||
sort bool
|
||||
sync bool
|
||||
nth *[]Range
|
||||
headerLines *int
|
||||
command *commandSpec
|
||||
environ []string
|
||||
changed bool
|
||||
denylist []int32
|
||||
revision revision
|
||||
}
|
||||
|
||||
type previewRequest struct {
|
||||
@@ -1022,6 +1029,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
sort: opts.Sort > 0,
|
||||
toggleSort: opts.ToggleSort,
|
||||
track: opts.Track,
|
||||
targetIndex: minItem.Index(),
|
||||
delimiter: opts.Delimiter,
|
||||
expect: opts.Expect,
|
||||
keymap: opts.Keymap,
|
||||
@@ -1063,7 +1071,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
headerFirst: opts.HeaderFirst,
|
||||
headerLines: opts.HeaderLines,
|
||||
gap: opts.Gap,
|
||||
header: []string{},
|
||||
header: []Item{},
|
||||
footer: opts.Footer,
|
||||
header0: opts.Header,
|
||||
ansi: opts.Ansi,
|
||||
@@ -1364,7 +1372,7 @@ func (t *Terminal) environImpl(forPreview bool) []string {
|
||||
}
|
||||
}
|
||||
env = append(env, "FZF_INPUT_STATE="+inputState)
|
||||
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
||||
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", max(0, t.count-t.headerLines)))
|
||||
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.resultMerger.Length()))
|
||||
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
|
||||
env = append(env, fmt.Sprintf("FZF_LINES=%d", t.areaLines))
|
||||
@@ -1755,8 +1763,14 @@ func (t *Terminal) changeFooter(footer string) {
|
||||
}
|
||||
|
||||
// UpdateHeader updates the header
|
||||
func (t *Terminal) UpdateHeader(header []string) {
|
||||
func (t *Terminal) UpdateHeader(header []Item) {
|
||||
t.mutex.Lock()
|
||||
// Pad to t.headerLines so that click coordinate mapping works correctly
|
||||
if len(header) < t.headerLines {
|
||||
padded := make([]Item, t.headerLines)
|
||||
copy(padded, header)
|
||||
header = padded
|
||||
}
|
||||
t.header = header
|
||||
t.mutex.Unlock()
|
||||
t.reqBox.Set(reqHeader, nil)
|
||||
@@ -1788,6 +1802,10 @@ func (t *Terminal) UpdateList(result MatchResult) {
|
||||
prevIndex = merger.First().item.Index()
|
||||
}
|
||||
}
|
||||
if t.targetIndex != minItem.Index() {
|
||||
prevIndex = t.targetIndex
|
||||
t.targetIndex = minItem.Index()
|
||||
}
|
||||
t.progress = 100
|
||||
t.merger = merger
|
||||
t.resultMerger = merger
|
||||
@@ -3079,11 +3097,11 @@ func (t *Terminal) printHeader() {
|
||||
}
|
||||
|
||||
t.withWindow(t.headerWindow, func() {
|
||||
var lines []string
|
||||
var headerItems []Item
|
||||
if !t.hasHeaderLinesWindow() {
|
||||
lines = t.header
|
||||
headerItems = t.header
|
||||
}
|
||||
t.printHeaderImpl(t.headerWindow, t.headerBorderShape, t.header0, lines)
|
||||
t.printHeaderImpl(t.headerWindow, t.headerBorderShape, t.header0, headerItems)
|
||||
})
|
||||
if w, shape := t.determineHeaderLinesShape(); w {
|
||||
t.withWindow(t.headerLinesWindow, func() {
|
||||
@@ -3145,7 +3163,7 @@ func (t *Terminal) headerIndentImpl(base int, borderShape tui.BorderShape) int {
|
||||
return indentSize
|
||||
}
|
||||
|
||||
func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShape, lines1 []string, lines2 []string) {
|
||||
func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShape, lines1 []string, lines2 []Item) {
|
||||
max := t.window.Height()
|
||||
if !t.inputless && t.inputWindow == nil && window == nil && t.headerFirst {
|
||||
max--
|
||||
@@ -3172,7 +3190,8 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
|
||||
}
|
||||
indent := strings.Repeat(" ", indentSize)
|
||||
t.wrap = false
|
||||
for idx, lineStr := range append(append([]string{}, lines1...), lines2...) {
|
||||
totalLines := len(lines1) + len(lines2)
|
||||
for idx := 0; idx < totalLines; idx++ {
|
||||
line := idx
|
||||
if needReverse && idx < len(lines1) {
|
||||
line = len(lines1) - idx - 1
|
||||
@@ -3186,11 +3205,18 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
|
||||
if line >= max {
|
||||
continue
|
||||
}
|
||||
trimmed, colors, newState := extractColor(lineStr, state, nil)
|
||||
state = newState
|
||||
item := &Item{
|
||||
text: util.ToChars([]byte(trimmed)),
|
||||
colors: colors}
|
||||
|
||||
var item *Item
|
||||
if idx < len(lines1) {
|
||||
trimmed, colors, newState := extractColor(lines1[idx], state, nil)
|
||||
state = newState
|
||||
item = &Item{
|
||||
text: util.ToChars([]byte(trimmed)),
|
||||
colors: colors}
|
||||
} else {
|
||||
headerItem := lines2[idx-len(lines1)]
|
||||
item = &headerItem
|
||||
}
|
||||
|
||||
t.printHighlighted(Result{item: item},
|
||||
tui.ColHeader, tui.ColHeader, false, false, false, line, line, true,
|
||||
@@ -5288,9 +5314,13 @@ func (t *Terminal) addClickHeaderWord(env []string) []string {
|
||||
return env
|
||||
}
|
||||
|
||||
// NOTE: t.header is padded with empty strings so that its size is equal to t.headerLines
|
||||
nthBase := 0
|
||||
headers := [2][]string{t.header, t.header0}
|
||||
// Convert header items to strings for click handling
|
||||
headerStrs := make([]string, len(t.header))
|
||||
for i, item := range t.header {
|
||||
headerStrs[i] = item.text.ToString()
|
||||
}
|
||||
headers := [2][]string{headerStrs, t.header0}
|
||||
if t.layout == layoutReverse {
|
||||
headers[0], headers[1] = headers[1], headers[0]
|
||||
}
|
||||
@@ -5892,6 +5922,7 @@ func (t *Terminal) Loop() error {
|
||||
events := []util.EventType{}
|
||||
changed := false
|
||||
var newNth *[]Range
|
||||
var newHeaderLines *int
|
||||
req := func(evts ...util.EventType) {
|
||||
for _, event := range evts {
|
||||
events = append(events, event)
|
||||
@@ -5908,6 +5939,7 @@ func (t *Terminal) Loop() error {
|
||||
events = []util.EventType{}
|
||||
changed = false
|
||||
newNth = nil
|
||||
newHeaderLines = nil
|
||||
beof := false
|
||||
queryChanged := false
|
||||
denylist := []int32{}
|
||||
@@ -6247,6 +6279,23 @@ func (t *Terminal) Loop() error {
|
||||
}
|
||||
case actPrintQuery:
|
||||
req(reqPrintQuery)
|
||||
case actChangeHeaderLines, actTransformHeaderLines, actBgTransformHeaderLines:
|
||||
capture(true, func(expr string) {
|
||||
if n, err := strconv.Atoi(expr); err == nil && n >= 0 && n != t.headerLines {
|
||||
t.headerLines = n
|
||||
newHeaderLines = &n
|
||||
changed = true
|
||||
// Deselect items that are now part of the header
|
||||
for idx := range t.selected {
|
||||
if idx < int32(n) {
|
||||
delete(t.selected, idx)
|
||||
}
|
||||
}
|
||||
// Tell UpdateList to reposition cursor to the current item
|
||||
t.targetIndex = t.currentIndex()
|
||||
req(reqList, reqPrompt, reqInfo, reqHeader)
|
||||
}
|
||||
})
|
||||
case actChangeMulti:
|
||||
multi := t.multi
|
||||
if a.a == "" {
|
||||
@@ -7428,7 +7477,7 @@ func (t *Terminal) Loop() error {
|
||||
reload := changed || newCommand != nil
|
||||
var reloadRequest *searchRequest
|
||||
if reload {
|
||||
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.resultMerger.Revision()}
|
||||
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, headerLines: newHeaderLines, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.resultMerger.Revision()}
|
||||
}
|
||||
|
||||
// Dispatch queued background requests
|
||||
|
||||
@@ -2176,6 +2176,80 @@ class TestCore < TestInteractive
|
||||
end
|
||||
end
|
||||
|
||||
def test_change_header_lines
|
||||
tmux.send_keys %(seq 10 | #{FZF} --header-lines 3 --bind 'space:change-header-lines(5),enter:transform-header-lines(echo 1)'), :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 7, lines.item_count
|
||||
assert lines.any_include?('> 4')
|
||||
end
|
||||
tmux.send_keys :Space
|
||||
tmux.until do |lines|
|
||||
assert_equal 5, lines.item_count
|
||||
assert lines.any_include?('> 6')
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 9, lines.item_count
|
||||
assert lines.any_include?('> 6')
|
||||
end
|
||||
end
|
||||
|
||||
def test_change_header_lines_to_zero
|
||||
tmux.send_keys %(seq 5 | #{FZF} --header-lines 3 --bind 'space:bg-transform-header-lines(echo 0)'), :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 2, lines.item_count
|
||||
assert lines.any_include?('> 4')
|
||||
end
|
||||
tmux.send_keys :Space
|
||||
tmux.until do |lines|
|
||||
assert_equal 5, lines.item_count
|
||||
# All items are now in the list, cursor stays on item 4
|
||||
assert lines.any_include?('> 4')
|
||||
end
|
||||
end
|
||||
|
||||
def test_change_header_lines_deselect
|
||||
# Selected items that become part of the header should be deselected
|
||||
tmux.send_keys %(seq 10 | #{FZF} --multi --header-lines 0 --bind 'space:change-header-lines(3),enter:change-header-lines(1)'), :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 10, lines.item_count
|
||||
assert lines.any_include?('> 1')
|
||||
end
|
||||
# Select items 1, 2, 3 (these will become header lines)
|
||||
tmux.send_keys :BTab, :BTab, :BTab
|
||||
tmux.until { |lines| assert_equal 3, lines.select_count }
|
||||
# Also select item 4 (this should remain selected)
|
||||
tmux.send_keys :BTab
|
||||
tmux.until { |lines| assert_equal 4, lines.select_count }
|
||||
# Change header-lines to 3: items 1, 2, 3 become headers and should be deselected
|
||||
tmux.send_keys :Space
|
||||
tmux.until do |lines|
|
||||
assert_equal 7, lines.item_count
|
||||
assert_equal 1, lines.select_count
|
||||
assert lines.any_include?('> 5')
|
||||
end
|
||||
# Change header-lines to 1
|
||||
tmux.send_keys :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 9, lines.item_count
|
||||
assert_equal 1, lines.select_count
|
||||
assert lines.any_include?('> 5')
|
||||
end
|
||||
end
|
||||
|
||||
def test_change_header_lines_reverse
|
||||
tmux.send_keys %(seq 10 | #{FZF} --header-lines 2 --reverse --bind 'space:change-header-lines(4)'), :Enter
|
||||
tmux.until do |lines|
|
||||
assert_equal 8, lines.item_count
|
||||
assert lines.any_include?('> 3')
|
||||
end
|
||||
tmux.send_keys :Space
|
||||
tmux.until do |lines|
|
||||
assert_equal 6, lines.item_count
|
||||
assert lines.any_include?('> 5')
|
||||
end
|
||||
end
|
||||
|
||||
def test_zero_width_characters
|
||||
tmux.send_keys %(for i in {1..1000}; do string+="a̱$i"; printf '\\e[43m%s\\e[0m\\n' "$string"; done | #{FZF} --ansi --query a500 --ellipsis XX), :Enter
|
||||
tmux.until do |lines|
|
||||
|
||||
@@ -326,4 +326,27 @@ class TestFilter < TestBase
|
||||
writelines(['emp001 Alice Engineering', 'emp002 Bob Marketing'])
|
||||
assert_equal 'emp001', `#{FZF} -d' ' --with-nth 2 --accept-nth 1 -f Alice < #{tempname}`.chomp
|
||||
end
|
||||
|
||||
def test_header_lines_filter
|
||||
assert_equal %w[4 5 6 7 8 9 10],
|
||||
`seq 10 | #{FZF} --header-lines 3 -f ""`.lines(chomp: true)
|
||||
assert_equal %w[5],
|
||||
`seq 10 | #{FZF} --header-lines 3 -f 5`.lines(chomp: true)
|
||||
# Header items should not be matched
|
||||
assert_empty `seq 10 | #{FZF} --header-lines 3 -f "^1$"`.lines(chomp: true)
|
||||
end
|
||||
|
||||
def test_header_lines_filter_with_nth
|
||||
writelines(%w[a:1 b:2 c:3 d:4 e:5])
|
||||
assert_equal %w[c:3 d:4 e:5],
|
||||
`#{FZF} --header-lines 2 -d: --with-nth 2 -f "" < #{tempname}`.lines(chomp: true)
|
||||
assert_equal %w[d:4],
|
||||
`#{FZF} --header-lines 2 -d: --with-nth 2 -f 4 < #{tempname}`.lines(chomp: true)
|
||||
end
|
||||
|
||||
def test_header_lines_all_headers
|
||||
# When all lines are header lines, no results
|
||||
assert_empty `seq 3 | #{FZF} --header-lines 10 -f ""`.chomp
|
||||
assert_equal 1, $CHILD_STATUS.exitstatus
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user