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
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
- 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)
\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\-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\-label(...)\fR (change \fB\-\-header\-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)
\fBtransform(...)\fR (transform states using the output of 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\-label(...)\fR (transform header 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[actCancel-17]
_ = x[actChangeBorderLabel-18]
_ = x[actChangeListLabel-19]
_ = x[actChangeInputLabel-20]
_ = x[actChangeHeader-21]
_ = x[actChangeHeaderLabel-22]
_ = x[actChangeMulti-23]
_ = x[actChangePreviewLabel-24]
_ = x[actChangePrompt-25]
_ = x[actChangeQuery-26]
_ = x[actChangeNth-27]
_ = x[actClearScreen-28]
_ = x[actClearQuery-29]
_ = x[actClearSelection-30]
_ = x[actClose-31]
_ = x[actDeleteChar-32]
_ = x[actDeleteCharEof-33]
_ = x[actEndOfLine-34]
_ = x[actFatal-35]
_ = x[actForwardChar-36]
_ = x[actForwardWord-37]
_ = x[actKillLine-38]
_ = x[actKillWord-39]
_ = x[actUnixLineDiscard-40]
_ = x[actUnixWordRubout-41]
_ = x[actYank-42]
_ = x[actBackwardKillWord-43]
_ = x[actSelectAll-44]
_ = x[actDeselectAll-45]
_ = x[actToggle-46]
_ = x[actToggleSearch-47]
_ = x[actToggleAll-48]
_ = x[actToggleDown-49]
_ = x[actToggleUp-50]
_ = x[actToggleIn-51]
_ = x[actToggleOut-52]
_ = x[actToggleTrack-53]
_ = x[actToggleTrackCurrent-54]
_ = x[actToggleHeader-55]
_ = x[actToggleWrap-56]
_ = x[actToggleMultiLine-57]
_ = x[actToggleHscroll-58]
_ = x[actTrackCurrent-59]
_ = x[actToggleInput-60]
_ = x[actHideInput-61]
_ = x[actShowInput-62]
_ = x[actUntrackCurrent-63]
_ = x[actDown-64]
_ = x[actUp-65]
_ = x[actPageUp-66]
_ = x[actPageDown-67]
_ = x[actPosition-68]
_ = x[actHalfPageUp-69]
_ = x[actHalfPageDown-70]
_ = x[actOffsetUp-71]
_ = x[actOffsetDown-72]
_ = x[actOffsetMiddle-73]
_ = x[actJump-74]
_ = x[actJumpAccept-75]
_ = x[actPrintQuery-76]
_ = x[actRefreshPreview-77]
_ = x[actReplaceQuery-78]
_ = x[actToggleSort-79]
_ = x[actShowPreview-80]
_ = x[actHidePreview-81]
_ = x[actTogglePreview-82]
_ = x[actTogglePreviewWrap-83]
_ = x[actTransform-84]
_ = x[actTransformBorderLabel-85]
_ = x[actTransformListLabel-86]
_ = x[actTransformInputLabel-87]
_ = x[actTransformHeader-88]
_ = x[actTransformHeaderLabel-89]
_ = x[actTransformNth-90]
_ = x[actTransformPointer-91]
_ = x[actTransformPreviewLabel-92]
_ = x[actTransformPrompt-93]
_ = x[actTransformQuery-94]
_ = x[actTransformSearch-95]
_ = x[actSearch-96]
_ = x[actPreview-97]
_ = x[actChangePointer-98]
_ = x[actChangePreview-99]
_ = x[actChangePreviewWindow-100]
_ = x[actPreviewTop-101]
_ = x[actPreviewBottom-102]
_ = x[actPreviewUp-103]
_ = x[actPreviewDown-104]
_ = x[actPreviewPageUp-105]
_ = x[actPreviewPageDown-106]
_ = x[actPreviewHalfPageUp-107]
_ = x[actPreviewHalfPageDown-108]
_ = x[actPrevHistory-109]
_ = x[actPrevSelected-110]
_ = x[actPrint-111]
_ = x[actPut-112]
_ = x[actNextHistory-113]
_ = x[actNextSelected-114]
_ = x[actExecute-115]
_ = x[actExecuteSilent-116]
_ = x[actExecuteMulti-117]
_ = x[actSigStop-118]
_ = x[actFirst-119]
_ = x[actLast-120]
_ = x[actReload-121]
_ = x[actReloadSync-122]
_ = x[actDisableSearch-123]
_ = x[actEnableSearch-124]
_ = x[actSelect-125]
_ = x[actDeselect-126]
_ = x[actUnbind-127]
_ = x[actRebind-128]
_ = x[actToggleBind-129]
_ = x[actBecome-130]
_ = x[actShowHeader-131]
_ = x[actHideHeader-132]
_ = x[actBell-133]
_ = x[actExclude-134]
_ = x[actExcludeMulti-135]
_ = x[actChangeGhost-19]
_ = x[actChangeHeader-20]
_ = x[actChangeHeaderLabel-21]
_ = x[actChangeInputLabel-22]
_ = x[actChangeListLabel-23]
_ = x[actChangeMulti-24]
_ = x[actChangeNth-25]
_ = x[actChangePointer-26]
_ = x[actChangePreview-27]
_ = x[actChangePreviewLabel-28]
_ = x[actChangePreviewWindow-29]
_ = x[actChangePrompt-30]
_ = x[actChangeQuery-31]
_ = x[actClearScreen-32]
_ = x[actClearQuery-33]
_ = x[actClearSelection-34]
_ = x[actClose-35]
_ = x[actDeleteChar-36]
_ = x[actDeleteCharEof-37]
_ = x[actEndOfLine-38]
_ = x[actFatal-39]
_ = x[actForwardChar-40]
_ = x[actForwardWord-41]
_ = x[actKillLine-42]
_ = x[actKillWord-43]
_ = x[actUnixLineDiscard-44]
_ = x[actUnixWordRubout-45]
_ = x[actYank-46]
_ = x[actBackwardKillWord-47]
_ = x[actSelectAll-48]
_ = x[actDeselectAll-49]
_ = x[actToggle-50]
_ = x[actToggleSearch-51]
_ = x[actToggleAll-52]
_ = x[actToggleDown-53]
_ = x[actToggleUp-54]
_ = x[actToggleIn-55]
_ = x[actToggleOut-56]
_ = x[actToggleTrack-57]
_ = x[actToggleTrackCurrent-58]
_ = x[actToggleHeader-59]
_ = x[actToggleWrap-60]
_ = x[actToggleMultiLine-61]
_ = x[actToggleHscroll-62]
_ = x[actTrackCurrent-63]
_ = x[actToggleInput-64]
_ = x[actHideInput-65]
_ = x[actShowInput-66]
_ = x[actUntrackCurrent-67]
_ = x[actDown-68]
_ = x[actUp-69]
_ = x[actPageUp-70]
_ = x[actPageDown-71]
_ = x[actPosition-72]
_ = x[actHalfPageUp-73]
_ = x[actHalfPageDown-74]
_ = x[actOffsetUp-75]
_ = x[actOffsetDown-76]
_ = x[actOffsetMiddle-77]
_ = x[actJump-78]
_ = x[actJumpAccept-79]
_ = x[actPrintQuery-80]
_ = x[actRefreshPreview-81]
_ = x[actReplaceQuery-82]
_ = x[actToggleSort-83]
_ = x[actShowPreview-84]
_ = x[actHidePreview-85]
_ = x[actTogglePreview-86]
_ = x[actTogglePreviewWrap-87]
_ = x[actTransform-88]
_ = x[actTransformBorderLabel-89]
_ = x[actTransformGhost-90]
_ = x[actTransformHeader-91]
_ = x[actTransformHeaderLabel-92]
_ = x[actTransformInputLabel-93]
_ = x[actTransformListLabel-94]
_ = x[actTransformNth-95]
_ = x[actTransformPointer-96]
_ = x[actTransformPreviewLabel-97]
_ = x[actTransformPrompt-98]
_ = x[actTransformQuery-99]
_ = x[actTransformSearch-100]
_ = x[actSearch-101]
_ = x[actPreview-102]
_ = x[actPreviewTop-103]
_ = x[actPreviewBottom-104]
_ = x[actPreviewUp-105]
_ = x[actPreviewDown-106]
_ = x[actPreviewPageUp-107]
_ = x[actPreviewPageDown-108]
_ = x[actPreviewHalfPageUp-109]
_ = x[actPreviewHalfPageDown-110]
_ = x[actPrevHistory-111]
_ = x[actPrevSelected-112]
_ = x[actPrint-113]
_ = x[actPut-114]
_ = x[actNextHistory-115]
_ = x[actNextSelected-116]
_ = x[actExecute-117]
_ = x[actExecuteSilent-118]
_ = x[actExecuteMulti-119]
_ = x[actSigStop-120]
_ = x[actFirst-121]
_ = x[actLast-122]
_ = x[actReload-123]
_ = x[actReloadSync-124]
_ = x[actDisableSearch-125]
_ = x[actEnableSearch-126]
_ = x[actSelect-127]
_ = x[actDeselect-128]
_ = x[actUnbind-129]
_ = x[actRebind-130]
_ = x[actToggleBind-131]
_ = x[actBecome-132]
_ = x[actShowHeader-133]
_ = x[actHideHeader-134]
_ = x[actBell-135]
_ = x[actExclude-136]
_ = x[actExcludeMulti-137]
}
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 {
if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@ -1404,7 +1404,7 @@ const (
func init() {
executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header|search|nth|pointer)|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("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@ -1799,6 +1799,8 @@ func isExecuteAction(str string) actionType {
return actChangeInputLabel
case "change-header-label":
return actChangeHeaderLabel
case "change-ghost":
return actChangeGhost
case "change-pointer":
return actChangePointer
case "change-preview-window":
@ -1839,6 +1841,8 @@ func isExecuteAction(str string) actionType {
return actTransformHeaderLabel
case "transform-header":
return actTransformHeader
case "transform-ghost":
return actTransformGhost
case "transform-nth":
return actTransformNth
case "transform-pointer":

View File

@ -475,15 +475,19 @@ const (
actBackwardWord
actCancel
actChangeBorderLabel
actChangeListLabel
actChangeInputLabel
actChangeGhost
actChangeHeader
actChangeHeaderLabel
actChangeInputLabel
actChangeListLabel
actChangeMulti
actChangeNth
actChangePointer
actChangePreview
actChangePreviewLabel
actChangePreviewWindow
actChangePrompt
actChangeQuery
actChangeNth
actClearScreen
actClearQuery
actClearSelection
@ -542,10 +546,11 @@ const (
actTogglePreviewWrap
actTransform
actTransformBorderLabel
actTransformListLabel
actTransformInputLabel
actTransformGhost
actTransformHeader
actTransformHeaderLabel
actTransformInputLabel
actTransformListLabel
actTransformNth
actTransformPointer
actTransformPreviewLabel
@ -554,9 +559,6 @@ const (
actTransformSearch
actSearch
actPreview
actChangePointer
actChangePreview
actChangePreviewWindow
actPreviewTop
actPreviewBottom
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:
pointer := a.a
if a.t == actTransformPointer {

View File

@ -1815,7 +1815,7 @@ class TestCore < TestInteractive
end
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|
assert_equal 100, lines.match_count
assert_includes lines, 'X Type in query ...'
@ -1830,6 +1830,10 @@ class TestCore < TestInteractive
assert_equal 100, lines.match_count
assert_includes lines, 'X Type in query ...'
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
def test_ghost_inline