Add change-ghost and transform-ghost

This commit is contained in:
Junegunn Choi 2025-03-28 23:35:20 +09:00
parent 0dce561ec9
commit ba6d1b8772
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
6 changed files with 153 additions and 129 deletions

View File

@ -8,6 +8,7 @@ CHANGELOG
# Display "Type to search" when the input is empty # Display "Type to search" when the input is empty
fzf --ghost "Type to search" fzf --ghost "Type to search"
``` ```
- Added `change-ghost` and `transform-ghost` actions for dynamically changing the ghost text
- Added `change-pointer` and `transform-pointer` actions for dynamically changing the pointer sign - Added `change-pointer` and `transform-pointer` actions for dynamically changing the pointer sign
- Bug fixes and improvements - Bug fixes and improvements

View File

@ -1609,6 +1609,7 @@ A key or an event can be bound to one or more of the following actions.
\fBbell\fR (ring the terminal bell) \fBbell\fR (ring the terminal bell)
\fBcancel\fR (clear query string if not empty, abort fzf otherwise) \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string) \fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string)
\fBchange\-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(...)\fR (change header to the given string; doesn't affect \fB\-\-header\-lines\fR)
\fBchange\-header\-label(...)\fR (change \fB\-\-header\-label\fR to the given string) \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\-input\-label(...)\fR (change \fB\-\-input\-label\fR to the given string)
@ -1705,6 +1706,7 @@ A key or an event can be bound to one or more of the following actions.
\fBtrack\-current\fR (track the current item; automatically disabled if focus changes) \fBtrack\-current\fR (track the current item; automatically disabled if focus changes)
\fBtransform(...)\fR (transform states using the output of an external command) \fBtransform(...)\fR (transform states using the output of an external command)
\fBtransform\-border\-label(...)\fR (transform border label using an external command) \fBtransform\-border\-label(...)\fR (transform border label using an external command)
\fBtransform\-ghost(...)\fR (transform ghost text using an external command)
\fBtransform\-header(...)\fR (transform header using an external command) \fBtransform\-header(...)\fR (transform header using an external command)
\fBtransform\-header\-label(...)\fR (transform header label 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\-input\-label(...)\fR (transform input label using an external command)

View File

@ -27,128 +27,130 @@ func _() {
_ = x[actBackwardWord-16] _ = x[actBackwardWord-16]
_ = x[actCancel-17] _ = x[actCancel-17]
_ = x[actChangeBorderLabel-18] _ = x[actChangeBorderLabel-18]
_ = x[actChangeListLabel-19] _ = x[actChangeGhost-19]
_ = x[actChangeInputLabel-20] _ = x[actChangeHeader-20]
_ = x[actChangeHeader-21] _ = x[actChangeHeaderLabel-21]
_ = x[actChangeHeaderLabel-22] _ = x[actChangeInputLabel-22]
_ = x[actChangeMulti-23] _ = x[actChangeListLabel-23]
_ = x[actChangePreviewLabel-24] _ = x[actChangeMulti-24]
_ = x[actChangePrompt-25] _ = x[actChangeNth-25]
_ = x[actChangeQuery-26] _ = x[actChangePointer-26]
_ = x[actChangeNth-27] _ = x[actChangePreview-27]
_ = x[actClearScreen-28] _ = x[actChangePreviewLabel-28]
_ = x[actClearQuery-29] _ = x[actChangePreviewWindow-29]
_ = x[actClearSelection-30] _ = x[actChangePrompt-30]
_ = x[actClose-31] _ = x[actChangeQuery-31]
_ = x[actDeleteChar-32] _ = x[actClearScreen-32]
_ = x[actDeleteCharEof-33] _ = x[actClearQuery-33]
_ = x[actEndOfLine-34] _ = x[actClearSelection-34]
_ = x[actFatal-35] _ = x[actClose-35]
_ = x[actForwardChar-36] _ = x[actDeleteChar-36]
_ = x[actForwardWord-37] _ = x[actDeleteCharEof-37]
_ = x[actKillLine-38] _ = x[actEndOfLine-38]
_ = x[actKillWord-39] _ = x[actFatal-39]
_ = x[actUnixLineDiscard-40] _ = x[actForwardChar-40]
_ = x[actUnixWordRubout-41] _ = x[actForwardWord-41]
_ = x[actYank-42] _ = x[actKillLine-42]
_ = x[actBackwardKillWord-43] _ = x[actKillWord-43]
_ = x[actSelectAll-44] _ = x[actUnixLineDiscard-44]
_ = x[actDeselectAll-45] _ = x[actUnixWordRubout-45]
_ = x[actToggle-46] _ = x[actYank-46]
_ = x[actToggleSearch-47] _ = x[actBackwardKillWord-47]
_ = x[actToggleAll-48] _ = x[actSelectAll-48]
_ = x[actToggleDown-49] _ = x[actDeselectAll-49]
_ = x[actToggleUp-50] _ = x[actToggle-50]
_ = x[actToggleIn-51] _ = x[actToggleSearch-51]
_ = x[actToggleOut-52] _ = x[actToggleAll-52]
_ = x[actToggleTrack-53] _ = x[actToggleDown-53]
_ = x[actToggleTrackCurrent-54] _ = x[actToggleUp-54]
_ = x[actToggleHeader-55] _ = x[actToggleIn-55]
_ = x[actToggleWrap-56] _ = x[actToggleOut-56]
_ = x[actToggleMultiLine-57] _ = x[actToggleTrack-57]
_ = x[actToggleHscroll-58] _ = x[actToggleTrackCurrent-58]
_ = x[actTrackCurrent-59] _ = x[actToggleHeader-59]
_ = x[actToggleInput-60] _ = x[actToggleWrap-60]
_ = x[actHideInput-61] _ = x[actToggleMultiLine-61]
_ = x[actShowInput-62] _ = x[actToggleHscroll-62]
_ = x[actUntrackCurrent-63] _ = x[actTrackCurrent-63]
_ = x[actDown-64] _ = x[actToggleInput-64]
_ = x[actUp-65] _ = x[actHideInput-65]
_ = x[actPageUp-66] _ = x[actShowInput-66]
_ = x[actPageDown-67] _ = x[actUntrackCurrent-67]
_ = x[actPosition-68] _ = x[actDown-68]
_ = x[actHalfPageUp-69] _ = x[actUp-69]
_ = x[actHalfPageDown-70] _ = x[actPageUp-70]
_ = x[actOffsetUp-71] _ = x[actPageDown-71]
_ = x[actOffsetDown-72] _ = x[actPosition-72]
_ = x[actOffsetMiddle-73] _ = x[actHalfPageUp-73]
_ = x[actJump-74] _ = x[actHalfPageDown-74]
_ = x[actJumpAccept-75] _ = x[actOffsetUp-75]
_ = x[actPrintQuery-76] _ = x[actOffsetDown-76]
_ = x[actRefreshPreview-77] _ = x[actOffsetMiddle-77]
_ = x[actReplaceQuery-78] _ = x[actJump-78]
_ = x[actToggleSort-79] _ = x[actJumpAccept-79]
_ = x[actShowPreview-80] _ = x[actPrintQuery-80]
_ = x[actHidePreview-81] _ = x[actRefreshPreview-81]
_ = x[actTogglePreview-82] _ = x[actReplaceQuery-82]
_ = x[actTogglePreviewWrap-83] _ = x[actToggleSort-83]
_ = x[actTransform-84] _ = x[actShowPreview-84]
_ = x[actTransformBorderLabel-85] _ = x[actHidePreview-85]
_ = x[actTransformListLabel-86] _ = x[actTogglePreview-86]
_ = x[actTransformInputLabel-87] _ = x[actTogglePreviewWrap-87]
_ = x[actTransformHeader-88] _ = x[actTransform-88]
_ = x[actTransformHeaderLabel-89] _ = x[actTransformBorderLabel-89]
_ = x[actTransformNth-90] _ = x[actTransformGhost-90]
_ = x[actTransformPointer-91] _ = x[actTransformHeader-91]
_ = x[actTransformPreviewLabel-92] _ = x[actTransformHeaderLabel-92]
_ = x[actTransformPrompt-93] _ = x[actTransformInputLabel-93]
_ = x[actTransformQuery-94] _ = x[actTransformListLabel-94]
_ = x[actTransformSearch-95] _ = x[actTransformNth-95]
_ = x[actSearch-96] _ = x[actTransformPointer-96]
_ = x[actPreview-97] _ = x[actTransformPreviewLabel-97]
_ = x[actChangePointer-98] _ = x[actTransformPrompt-98]
_ = x[actChangePreview-99] _ = x[actTransformQuery-99]
_ = x[actChangePreviewWindow-100] _ = x[actTransformSearch-100]
_ = x[actPreviewTop-101] _ = x[actSearch-101]
_ = x[actPreviewBottom-102] _ = x[actPreview-102]
_ = x[actPreviewUp-103] _ = x[actPreviewTop-103]
_ = x[actPreviewDown-104] _ = x[actPreviewBottom-104]
_ = x[actPreviewPageUp-105] _ = x[actPreviewUp-105]
_ = x[actPreviewPageDown-106] _ = x[actPreviewDown-106]
_ = x[actPreviewHalfPageUp-107] _ = x[actPreviewPageUp-107]
_ = x[actPreviewHalfPageDown-108] _ = x[actPreviewPageDown-108]
_ = x[actPrevHistory-109] _ = x[actPreviewHalfPageUp-109]
_ = x[actPrevSelected-110] _ = x[actPreviewHalfPageDown-110]
_ = x[actPrint-111] _ = x[actPrevHistory-111]
_ = x[actPut-112] _ = x[actPrevSelected-112]
_ = x[actNextHistory-113] _ = x[actPrint-113]
_ = x[actNextSelected-114] _ = x[actPut-114]
_ = x[actExecute-115] _ = x[actNextHistory-115]
_ = x[actExecuteSilent-116] _ = x[actNextSelected-116]
_ = x[actExecuteMulti-117] _ = x[actExecute-117]
_ = x[actSigStop-118] _ = x[actExecuteSilent-118]
_ = x[actFirst-119] _ = x[actExecuteMulti-119]
_ = x[actLast-120] _ = x[actSigStop-120]
_ = x[actReload-121] _ = x[actFirst-121]
_ = x[actReloadSync-122] _ = x[actLast-122]
_ = x[actDisableSearch-123] _ = x[actReload-123]
_ = x[actEnableSearch-124] _ = x[actReloadSync-124]
_ = x[actSelect-125] _ = x[actDisableSearch-125]
_ = x[actDeselect-126] _ = x[actEnableSearch-126]
_ = x[actUnbind-127] _ = x[actSelect-127]
_ = x[actRebind-128] _ = x[actDeselect-128]
_ = x[actToggleBind-129] _ = x[actUnbind-129]
_ = x[actBecome-130] _ = x[actRebind-130]
_ = x[actShowHeader-131] _ = x[actToggleBind-131]
_ = x[actHideHeader-132] _ = x[actBecome-132]
_ = x[actBell-133] _ = x[actShowHeader-133]
_ = x[actExclude-134] _ = x[actHideHeader-134]
_ = x[actExcludeMulti-135] _ = x[actBell-135]
_ = x[actExclude-136]
_ = x[actExcludeMulti-137]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactChangePointeractChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti" const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeHeaderLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformHeaderLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 287, 306, 321, 341, 355, 376, 391, 405, 417, 431, 444, 461, 469, 482, 498, 510, 518, 532, 546, 557, 568, 586, 603, 610, 629, 641, 655, 664, 679, 691, 704, 715, 726, 738, 752, 773, 788, 801, 819, 835, 850, 864, 876, 888, 905, 912, 917, 926, 937, 948, 961, 976, 987, 1000, 1015, 1022, 1035, 1048, 1065, 1080, 1093, 1107, 1121, 1137, 1157, 1169, 1192, 1213, 1235, 1253, 1276, 1291, 1310, 1334, 1352, 1369, 1387, 1396, 1406, 1422, 1438, 1460, 1473, 1489, 1501, 1515, 1531, 1549, 1569, 1591, 1605, 1620, 1628, 1634, 1648, 1663, 1673, 1689, 1704, 1714, 1722, 1729, 1738, 1751, 1767, 1782, 1791, 1802, 1811, 1820, 1833, 1842, 1855, 1868, 1875, 1885, 1900} 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}
func (i actionType) String() string { func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) { if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@ -1404,7 +1404,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)|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)-label|header|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-]+")
} }
@ -1799,6 +1799,8 @@ func isExecuteAction(str string) actionType {
return actChangeInputLabel return actChangeInputLabel
case "change-header-label": case "change-header-label":
return actChangeHeaderLabel return actChangeHeaderLabel
case "change-ghost":
return actChangeGhost
case "change-pointer": case "change-pointer":
return actChangePointer return actChangePointer
case "change-preview-window": case "change-preview-window":
@ -1839,6 +1841,8 @@ func isExecuteAction(str string) actionType {
return actTransformHeaderLabel return actTransformHeaderLabel
case "transform-header": case "transform-header":
return actTransformHeader return actTransformHeader
case "transform-ghost":
return actTransformGhost
case "transform-nth": case "transform-nth":
return actTransformNth return actTransformNth
case "transform-pointer": case "transform-pointer":

View File

@ -475,15 +475,19 @@ const (
actBackwardWord actBackwardWord
actCancel actCancel
actChangeBorderLabel actChangeBorderLabel
actChangeListLabel actChangeGhost
actChangeInputLabel
actChangeHeader actChangeHeader
actChangeHeaderLabel actChangeHeaderLabel
actChangeInputLabel
actChangeListLabel
actChangeMulti actChangeMulti
actChangeNth
actChangePointer
actChangePreview
actChangePreviewLabel actChangePreviewLabel
actChangePreviewWindow
actChangePrompt actChangePrompt
actChangeQuery actChangeQuery
actChangeNth
actClearScreen actClearScreen
actClearQuery actClearQuery
actClearSelection actClearSelection
@ -542,10 +546,11 @@ const (
actTogglePreviewWrap actTogglePreviewWrap
actTransform actTransform
actTransformBorderLabel actTransformBorderLabel
actTransformListLabel actTransformGhost
actTransformInputLabel
actTransformHeader actTransformHeader
actTransformHeaderLabel actTransformHeaderLabel
actTransformInputLabel
actTransformListLabel
actTransformNth actTransformNth
actTransformPointer actTransformPointer
actTransformPreviewLabel actTransformPreviewLabel
@ -554,9 +559,6 @@ const (
actTransformSearch actTransformSearch
actSearch actSearch
actPreview actPreview
actChangePointer
actChangePreview
actChangePreviewWindow
actPreviewTop actPreviewTop
actPreviewBottom actPreviewBottom
actPreviewUp actPreviewUp
@ -5958,6 +5960,15 @@ func (t *Terminal) Loop() error {
} }
} }
} }
case actChangeGhost, actTransformGhost:
ghost := a.a
if a.t == actTransformGhost {
ghost = t.captureLine(a.a)
}
t.ghost = ghost
if len(t.input) == 0 {
req(reqPrompt)
}
case actChangePointer, actTransformPointer: case actChangePointer, actTransformPointer:
pointer := a.a pointer := a.a
if a.t == actTransformPointer { if a.t == actTransformPointer {

View File

@ -1815,7 +1815,7 @@ class TestCore < TestInteractive
end end
def test_ghost def test_ghost
tmux.send_keys %(seq 100 | #{FZF} --prompt 'X ' --ghost 'Type in query ...'), :Enter tmux.send_keys %(seq 100 | #{FZF} --prompt 'X ' --ghost 'Type in query ...' --bind 'space:change-ghost:Y Z' --bind 'enter:transform-ghost:echo Z Y'), :Enter
tmux.until do |lines| tmux.until do |lines|
assert_equal 100, lines.match_count assert_equal 100, lines.match_count
assert_includes lines, 'X Type in query ...' assert_includes lines, 'X Type in query ...'
@ -1830,6 +1830,10 @@ class TestCore < TestInteractive
assert_equal 100, lines.match_count assert_equal 100, lines.match_count
assert_includes lines, 'X Type in query ...' assert_includes lines, 'X Type in query ...'
end end
tmux.send_keys :Space
tmux.until { |lines| assert_includes lines, 'X Y Z' }
tmux.send_keys :Enter
tmux.until { |lines| assert_includes lines, 'X Z Y' }
end end
def test_ghost_inline def test_ghost_inline