Add {show,hide,toggle}-input and expose $FZF_INPUT_STATE

This commit is contained in:
Junegunn Choi
2025-02-01 11:16:16 +09:00
parent e1e171a3c4
commit e84afe196a
9 changed files with 156 additions and 81 deletions

View File

@@ -35,6 +35,7 @@ CHANGELOG
--header-border bottom --input-border \ --header-border bottom --input-border \
--bind 'click-header:transform-search:echo ${FZF_CLICK_HEADER_WORD:1:-1}' --bind 'click-header:transform-search:echo ${FZF_CLICK_HEADER_WORD:1:-1}'
``` ```
- You can later show the input section using `show-input` or `toggle-input` action, and hide it again using `hide-input`, or `toggle-input`.
- Extended `{q}` placeholder to support ranges. e.g. `{q:1}`, `{q:2..}`, etc. - Extended `{q}` placeholder to support ranges. e.g. `{q:1}`, `{q:2..}`, etc.
- Added `search(...)` and `transform-search(...)` action to trigger an fzf search with an arbitrary query string. This can be used to extend the search syntax of fzf. In the following example, fzf will use the first word of the query to trigger ripgrep search, and use the rest of the query to perform fzf search within the result. - Added `search(...)` and `transform-search(...)` action to trigger an fzf search with an arbitrary query string. This can be used to extend the search syntax of fzf. In the following example, fzf will use the first word of the query to trigger ripgrep search, and use the rest of the query to perform fzf search within the result.
```sh ```sh

View File

@@ -623,7 +623,9 @@ Position of the list label
.TP .TP
.B "\-\-no\-input" .B "\-\-no\-input"
Disable and hide the input section. You can no longer type in queries. To Disable and hide the input section. You can no longer type in queries. To
trigger a search, use \fBsearch\fR action. trigger a search, use \fBsearch\fR action. You can later show the input section
using \fBshow\-input\fR or \fBtoggle\-input\fR action, and hide it again using
\fBhide\-input\fR, or \fBtoggle\-input\fR.
.TP .TP
.BI "\-\-prompt=" "STR" .BI "\-\-prompt=" "STR"
@@ -1222,6 +1224,8 @@ fzf exports the following environment variables to its child processes.
.br .br
.BR FZF_QUERY " Current query string" .BR FZF_QUERY " Current query string"
.br .br
.BR FZF_INPUT_STATE " Current input state (enabled, disabled, hidden)"
.br
.BR FZF_NTH " Current \-\-nth option" .BR FZF_NTH " Current \-\-nth option"
.br .br
.BR FZF_PROMPT " Prompt string" .BR FZF_PROMPT " Prompt string"
@@ -1610,6 +1614,7 @@ A key or an event can be bound to one or more of the following actions.
\fBhalf\-page\-down\fR \fBhalf\-page\-down\fR
\fBhalf\-page\-up\fR \fBhalf\-page\-up\fR
\fBhide\-header\fR \fBhide\-header\fR
\fBhide\-input\fR
\fBhide\-preview\fR \fBhide\-preview\fR
\fBoffset\-down\fR (similar to CTRL\-E of Vim) \fBoffset\-down\fR (similar to CTRL\-E of Vim)
\fBoffset\-up\fR (similar to CTRL\-Y of Vim) \fBoffset\-up\fR (similar to CTRL\-Y of Vim)
@@ -1638,6 +1643,7 @@ A key or an event can be bound to one or more of the following actions.
\fBselect\fR \fBselect\fR
\fBselect\-all\fR (select all matches) \fBselect\-all\fR (select all matches)
\fBshow\-header\fR \fBshow\-header\fR
\fBshow\-input\fR
\fBshow\-preview\fR \fBshow\-preview\fR
\fBtoggle\fR (\fIright\-click\fR) \fBtoggle\fR (\fIright\-click\fR)
\fBtoggle\-all\fR (toggle all matches) \fBtoggle\-all\fR (toggle all matches)
@@ -1646,6 +1652,7 @@ A key or an event can be bound to one or more of the following actions.
\fBtoggle\-bind\fR \fBtoggle\-bind\fR
\fBtoggle\-header\fR \fBtoggle\-header\fR
\fBtoggle\-hscroll\fR \fBtoggle\-hscroll\fR
\fBtoggle\-input\fR
\fBtoggle\-multi\-line\fR \fBtoggle\-multi\-line\fR
\fBtoggle\-preview\fR \fBtoggle\-preview\fR
\fBtoggle\-preview\-wrap\fR \fBtoggle\-preview\-wrap\fR

View File

@@ -66,80 +66,83 @@ func _() {
_ = x[actToggleMultiLine-55] _ = x[actToggleMultiLine-55]
_ = x[actToggleHscroll-56] _ = x[actToggleHscroll-56]
_ = x[actTrackCurrent-57] _ = x[actTrackCurrent-57]
_ = x[actUntrackCurrent-58] _ = x[actToggleInput-58]
_ = x[actDown-59] _ = x[actHideInput-59]
_ = x[actUp-60] _ = x[actShowInput-60]
_ = x[actPageUp-61] _ = x[actUntrackCurrent-61]
_ = x[actPageDown-62] _ = x[actDown-62]
_ = x[actPosition-63] _ = x[actUp-63]
_ = x[actHalfPageUp-64] _ = x[actPageUp-64]
_ = x[actHalfPageDown-65] _ = x[actPageDown-65]
_ = x[actOffsetUp-66] _ = x[actPosition-66]
_ = x[actOffsetDown-67] _ = x[actHalfPageUp-67]
_ = x[actOffsetMiddle-68] _ = x[actHalfPageDown-68]
_ = x[actJump-69] _ = x[actOffsetUp-69]
_ = x[actJumpAccept-70] _ = x[actOffsetDown-70]
_ = x[actPrintQuery-71] _ = x[actOffsetMiddle-71]
_ = x[actRefreshPreview-72] _ = x[actJump-72]
_ = x[actReplaceQuery-73] _ = x[actJumpAccept-73]
_ = x[actToggleSort-74] _ = x[actPrintQuery-74]
_ = x[actShowPreview-75] _ = x[actRefreshPreview-75]
_ = x[actHidePreview-76] _ = x[actReplaceQuery-76]
_ = x[actTogglePreview-77] _ = x[actToggleSort-77]
_ = x[actTogglePreviewWrap-78] _ = x[actShowPreview-78]
_ = x[actTransform-79] _ = x[actHidePreview-79]
_ = x[actTransformBorderLabel-80] _ = x[actTogglePreview-80]
_ = x[actTransformListLabel-81] _ = x[actTogglePreviewWrap-81]
_ = x[actTransformInputLabel-82] _ = x[actTransform-82]
_ = x[actTransformHeader-83] _ = x[actTransformBorderLabel-83]
_ = x[actTransformHeaderLabel-84] _ = x[actTransformListLabel-84]
_ = x[actTransformNth-85] _ = x[actTransformInputLabel-85]
_ = x[actTransformPreviewLabel-86] _ = x[actTransformHeader-86]
_ = x[actTransformPrompt-87] _ = x[actTransformHeaderLabel-87]
_ = x[actTransformQuery-88] _ = x[actTransformNth-88]
_ = x[actTransformSearch-89] _ = x[actTransformPreviewLabel-89]
_ = x[actSearch-90] _ = x[actTransformPrompt-90]
_ = x[actPreview-91] _ = x[actTransformQuery-91]
_ = x[actChangePreview-92] _ = x[actTransformSearch-92]
_ = x[actChangePreviewWindow-93] _ = x[actSearch-93]
_ = x[actPreviewTop-94] _ = x[actPreview-94]
_ = x[actPreviewBottom-95] _ = x[actChangePreview-95]
_ = x[actPreviewUp-96] _ = x[actChangePreviewWindow-96]
_ = x[actPreviewDown-97] _ = x[actPreviewTop-97]
_ = x[actPreviewPageUp-98] _ = x[actPreviewBottom-98]
_ = x[actPreviewPageDown-99] _ = x[actPreviewUp-99]
_ = x[actPreviewHalfPageUp-100] _ = x[actPreviewDown-100]
_ = x[actPreviewHalfPageDown-101] _ = x[actPreviewPageUp-101]
_ = x[actPrevHistory-102] _ = x[actPreviewPageDown-102]
_ = x[actPrevSelected-103] _ = x[actPreviewHalfPageUp-103]
_ = x[actPrint-104] _ = x[actPreviewHalfPageDown-104]
_ = x[actPut-105] _ = x[actPrevHistory-105]
_ = x[actNextHistory-106] _ = x[actPrevSelected-106]
_ = x[actNextSelected-107] _ = x[actPrint-107]
_ = x[actExecute-108] _ = x[actPut-108]
_ = x[actExecuteSilent-109] _ = x[actNextHistory-109]
_ = x[actExecuteMulti-110] _ = x[actNextSelected-110]
_ = x[actSigStop-111] _ = x[actExecute-111]
_ = x[actFirst-112] _ = x[actExecuteSilent-112]
_ = x[actLast-113] _ = x[actExecuteMulti-113]
_ = x[actReload-114] _ = x[actSigStop-114]
_ = x[actReloadSync-115] _ = x[actFirst-115]
_ = x[actDisableSearch-116] _ = x[actLast-116]
_ = x[actEnableSearch-117] _ = x[actReload-117]
_ = x[actSelect-118] _ = x[actReloadSync-118]
_ = x[actDeselect-119] _ = x[actDisableSearch-119]
_ = x[actUnbind-120] _ = x[actEnableSearch-120]
_ = x[actRebind-121] _ = x[actSelect-121]
_ = x[actToggleBind-122] _ = x[actDeselect-122]
_ = x[actBecome-123] _ = x[actUnbind-123]
_ = x[actShowHeader-124] _ = x[actRebind-124]
_ = x[actHideHeader-125] _ = x[actToggleBind-125]
_ = x[actBell-126] _ = x[actBecome-126]
_ = x[actShowHeader-127]
_ = x[actHideHeader-128]
_ = x[actBell-129]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformNthactTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBell" const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformNthactTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBell"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 299, 313, 334, 349, 363, 375, 389, 402, 419, 427, 440, 456, 468, 476, 490, 504, 515, 526, 544, 561, 568, 587, 599, 613, 622, 637, 649, 662, 673, 684, 696, 710, 731, 746, 759, 777, 793, 808, 825, 832, 837, 846, 857, 868, 881, 896, 907, 920, 935, 942, 955, 968, 985, 1000, 1013, 1027, 1041, 1057, 1077, 1089, 1112, 1133, 1155, 1173, 1196, 1211, 1235, 1253, 1270, 1288, 1297, 1307, 1323, 1345, 1358, 1374, 1386, 1400, 1416, 1434, 1454, 1476, 1490, 1505, 1513, 1519, 1533, 1548, 1558, 1574, 1589, 1599, 1607, 1614, 1623, 1636, 1652, 1667, 1676, 1687, 1696, 1705, 1718, 1727, 1740, 1753, 1760} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 299, 313, 334, 349, 363, 375, 389, 402, 419, 427, 440, 456, 468, 476, 490, 504, 515, 526, 544, 561, 568, 587, 599, 613, 622, 637, 649, 662, 673, 684, 696, 710, 731, 746, 759, 777, 793, 808, 822, 834, 846, 863, 870, 875, 884, 895, 906, 919, 934, 945, 958, 973, 980, 993, 1006, 1023, 1038, 1051, 1065, 1079, 1095, 1115, 1127, 1150, 1171, 1193, 1211, 1234, 1249, 1273, 1291, 1308, 1326, 1335, 1345, 1361, 1383, 1396, 1412, 1424, 1438, 1454, 1472, 1492, 1514, 1528, 1543, 1551, 1557, 1571, 1586, 1596, 1612, 1627, 1637, 1645, 1652, 1661, 1674, 1690, 1705, 1714, 1725, 1734, 1743, 1756, 1765, 1778, 1791, 1798}
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

@@ -1500,6 +1500,12 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actToggleTrack) appendAction(actToggleTrack)
case "toggle-track-current": case "toggle-track-current":
appendAction(actToggleTrackCurrent) appendAction(actToggleTrackCurrent)
case "toggle-input":
appendAction(actToggleInput)
case "hide-input":
appendAction(actHideInput)
case "show-input":
appendAction(actShowInput)
case "toggle-header": case "toggle-header":
appendAction(actToggleHeader) appendAction(actToggleHeader)
case "toggle-wrap": case "toggle-wrap":

View File

@@ -505,6 +505,9 @@ const (
actToggleMultiLine actToggleMultiLine
actToggleHscroll actToggleHscroll
actTrackCurrent actTrackCurrent
actToggleInput
actHideInput
actShowInput
actUntrackCurrent actUntrackCurrent
actDown actDown
actUp actUp
@@ -1064,6 +1067,13 @@ func (t *Terminal) environImpl(forPreview bool) []string {
if len(t.nthCurrent) > 0 { if len(t.nthCurrent) > 0 {
env = append(env, "FZF_NTH="+RangesToString(t.nthCurrent)) env = append(env, "FZF_NTH="+RangesToString(t.nthCurrent))
} }
inputState := "enabled"
if t.inputless {
inputState = "hidden"
} else if t.paused {
inputState = "disabled"
}
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", t.count))
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length())) env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected))) env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
@@ -2492,22 +2502,29 @@ func (t *Terminal) printInfoImpl() {
} }
} }
func (t *Terminal) printHeader() { func (t *Terminal) resizeIfNeeded() bool {
// Check if input border is used and input has changed
if t.inputBorderShape.Visible() && t.inputWindow == nil && !t.inputless {
t.printAll()
return true
}
// Check if the header borders are used and header has changed
allHeaderLines := t.visibleHeaderLines() allHeaderLines := t.visibleHeaderLines()
primaryHeaderLines := allHeaderLines primaryHeaderLines := allHeaderLines
if t.headerLinesShape.Visible() { if t.headerLinesShape.Visible() {
primaryHeaderLines -= t.headerLines primaryHeaderLines -= t.headerLines
} }
// We may need to resize header windows
if (t.headerBorderShape.Visible() || t.headerLinesShape.Visible()) && if (t.headerBorderShape.Visible() || t.headerLinesShape.Visible()) &&
(t.headerWindow == nil && primaryHeaderLines > 0 || t.headerWindow != nil && primaryHeaderLines != t.headerWindow.Height()) || (t.headerWindow == nil && primaryHeaderLines > 0 || t.headerWindow != nil && primaryHeaderLines != t.headerWindow.Height()) ||
t.headerLinesShape.Visible() && (t.headerLinesWindow == nil && t.headerLines > 0 || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) { t.headerLinesShape.Visible() && (t.headerLinesWindow == nil && t.headerLines > 0 || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) {
t.resizeWindows(false, true) t.printAll()
t.printList() return true
t.printPrompt()
t.printInfo()
t.printPreview()
} }
return false
}
func (t *Terminal) printHeader() {
if !t.headerVisible { if !t.headerVisible {
return return
} }
@@ -4498,7 +4515,11 @@ func (t *Terminal) Loop() error {
// U t.uiMutex | // U t.uiMutex |
t.uiMutex.Lock() t.uiMutex.Lock()
t.mutex.Lock() t.mutex.Lock()
printInfo := util.RunOnce(t.printInfo) printInfo := util.RunOnce(func() {
if !t.resizeIfNeeded() {
t.printInfo()
}
})
for _, key := range keys { for _, key := range keys {
req := util.EventType(key) req := util.EventType(key)
value := (*events)[req] value := (*events)[req]
@@ -4540,7 +4561,9 @@ func (t *Terminal) Loop() error {
} }
t.printList() t.printList()
case reqHeader: case reqHeader:
t.printHeader() if !t.resizeIfNeeded() {
t.printHeader()
}
case reqActivate: case reqActivate:
t.suppress = false t.suppress = false
if t.hasPreviewer() { if t.hasPreviewer() {
@@ -4796,6 +4819,7 @@ func (t *Terminal) Loop() error {
return true return true
} }
doAction = func(a *action) bool { doAction = func(a *action) bool {
Action:
switch a.t { switch a.t {
case actIgnore, actStart, actClick: case actIgnore, actStart, actClick:
case actBecome: case actBecome:
@@ -5416,6 +5440,28 @@ func (t *Terminal) Loop() error {
t.forceRerenderList() t.forceRerenderList()
t.hscroll = !t.hscroll t.hscroll = !t.hscroll
req(reqList) req(reqList)
case actToggleInput, actShowInput, actHideInput:
switch a.t {
case actToggleInput:
t.inputless = !t.inputless
case actShowInput:
if !t.inputless {
break Action
}
t.inputless = false
case actHideInput:
if t.inputless {
break Action
}
t.inputless = true
}
t.forceRerenderList()
if t.inputless {
t.tui.HideCursor()
} else {
t.tui.ShowCursor()
}
req(reqList, reqInfo, reqPrompt, reqHeader)
case actTrackCurrent: case actTrackCurrent:
if t.track == trackDisabled { if t.track == trackDisabled {
t.track = trackCurrent t.track = trackCurrent

View File

@@ -46,6 +46,7 @@ func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false
func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool { return false } func (r *FullscreenRenderer) ShouldEmitResizeEvent() bool { return false }
func (r *FullscreenRenderer) Bell() {} func (r *FullscreenRenderer) Bell() {}
func (r *FullscreenRenderer) HideCursor() {} func (r *FullscreenRenderer) HideCursor() {}
func (r *FullscreenRenderer) ShowCursor() {}
func (r *FullscreenRenderer) Refresh() {} func (r *FullscreenRenderer) Refresh() {}
func (r *FullscreenRenderer) Close() {} func (r *FullscreenRenderer) Close() {}
func (r *FullscreenRenderer) Size() TermSize { return TermSize{} } func (r *FullscreenRenderer) Size() TermSize { return TermSize{} }

View File

@@ -1228,4 +1228,10 @@ func (w *LightWindow) EraseMaybe() bool {
func (r *LightRenderer) HideCursor() { func (r *LightRenderer) HideCursor() {
r.showCursor = false r.showCursor = false
r.csi("?25l")
}
func (r *LightRenderer) ShowCursor() {
r.showCursor = true
r.csi("?25h")
} }

View File

@@ -111,6 +111,10 @@ func (r *FullscreenRenderer) HideCursor() {
r.showCursor = false r.showCursor = false
} }
func (r *FullscreenRenderer) ShowCursor() {
r.showCursor = true
}
func (r *FullscreenRenderer) PassThrough(str string) { func (r *FullscreenRenderer) PassThrough(str string) {
// No-op // No-op
// https://github.com/gdamore/tcell/pull/650#issuecomment-1806442846 // https://github.com/gdamore/tcell/pull/650#issuecomment-1806442846

View File

@@ -616,6 +616,7 @@ type Renderer interface {
ShouldEmitResizeEvent() bool ShouldEmitResizeEvent() bool
Bell() Bell()
HideCursor() HideCursor()
ShowCursor()
GetChar() Event GetChar() Event