mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-15 06:43:47 -05:00
Introduce 'raw' mode
This commit is contained in:
74
CHANGELOG.md
74
CHANGELOG.md
@@ -3,11 +3,69 @@ CHANGELOG
|
|||||||
|
|
||||||
0.66.0
|
0.66.0
|
||||||
------
|
------
|
||||||
- Style changes
|
|
||||||
- Updated `--color base16` (alias: `16`) theme so that it works better with both dark and light themes.
|
### Introducing "raw" mode
|
||||||
|
|
||||||
|
In "raw" mode, non-matching items are also displayed in their original position,
|
||||||
|
but dimmed. This is useful when you want to see the surrounding items of a match
|
||||||
|
to understand the context. Raw mode can be enabled by using `--raw` option, but
|
||||||
|
I find it more useful when toggled dynamically using `toggle-raw` action.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export FZF_CTRL_R_OPTS='--bind ctrl-x:toggle-raw'
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
tree | fzf --raw --reverse --bind ctrl-x:toggle-raw
|
||||||
|
```
|
||||||
|
|
||||||
|
While non-matching items are displayed in dimmed color, they are treated just
|
||||||
|
like matching items in the list, so you place the cursor on them and do any
|
||||||
|
action against them. But if you want to navigate through matching items only,
|
||||||
|
you can use `down-match` and `up-match` actions, which are from now on bound to
|
||||||
|
`CTRL-N` and `CTRL-P` respectively. Historically, `CTRL-N` and `CTRL-P` are
|
||||||
|
bound to `next-history` and `prev-history` when `--history` option is used, so
|
||||||
|
in that case, you'll have to manually bind the actions to the keys of your
|
||||||
|
choice, or you can use `ALT-DOWN` and `ALT-UP` instead.
|
||||||
|
|
||||||
|
#### Customizing the look
|
||||||
|
|
||||||
|
##### Gutter
|
||||||
|
|
||||||
|
To distinguish the raw mode, the gutter column is rendered in dashed line using
|
||||||
|
`▖` character. But you can customize it using `--gutter-raw CHAR` option.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# If you don't liked the dashed line and you just want a thinner gutter
|
||||||
|
fzf --bind ctrl-x:toggle-raw --gutter-raw ▎
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Color and style of non-matching items
|
||||||
|
|
||||||
|
Non-matching items are displayed in dimmed color by default, but you can change
|
||||||
|
it using `--color hidden:...` option.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
fzf --raw --color hidden:red:strikethrough
|
||||||
|
|
||||||
|
# To unset the default 'dim' attribute, prefix the color spec with 'regular'
|
||||||
|
fzf --raw --color hidden:regular:red:strikethrough
|
||||||
|
```
|
||||||
|
|
||||||
|
### Style changes
|
||||||
|
|
||||||
|
This version introduces some minor changes to the traditional visual style of fzf.
|
||||||
|
|
||||||
- Narrowed the gutter column by using the left-half block character (`▌`).
|
- Narrowed the gutter column by using the left-half block character (`▌`).
|
||||||
- Removed background colors from markers.
|
- Removed background colors from markers.
|
||||||
- Added `--gutter CHAR` option for customizing the gutter column. Some examples using [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_characters):
|
- Updated `--color base16` (alias: `16`) theme so that it works better with both dark and light themes.
|
||||||
|
|
||||||
|
### Added options
|
||||||
|
|
||||||
|
#### `--gutter CHAR`
|
||||||
|
|
||||||
|
Added `--gutter CHAR` option for customizing the gutter column. Some examples using [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_characters):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Right-aligned gutter
|
# Right-aligned gutter
|
||||||
fzf --gutter '▐'
|
fzf --gutter '▐'
|
||||||
@@ -28,6 +86,16 @@ CHANGELOG
|
|||||||
fzf --gutter ' '
|
fzf --gutter ' '
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `--gutter-raw CHAR`
|
||||||
|
|
||||||
|
As mentioned above, also added `--gutter-raw CHAR` option for customizing the gutter column in raw mode.
|
||||||
|
|
||||||
|
### Compatibility changes
|
||||||
|
|
||||||
|
Starting from this version, fzf is built with Go 1.23. Support for some old OS versions has been dropped.
|
||||||
|
|
||||||
|
See https://go.dev/wiki/MinimumRequirements.
|
||||||
|
|
||||||
0.65.2
|
0.65.2
|
||||||
------
|
------
|
||||||
- Bug fixes and improvements
|
- Bug fixes and improvements
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
|
|||||||
\fBheader (header\-fg) \fRHeader
|
\fBheader (header\-fg) \fRHeader
|
||||||
\fBfooter (footer\-fg) \fRFooter
|
\fBfooter (footer\-fg) \fRFooter
|
||||||
\fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes)
|
\fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes)
|
||||||
|
\fBhidden \fRNon-matching items in raw mode (default: \fBdim\fR)
|
||||||
|
|
||||||
.B ANSI COLORS:
|
.B ANSI COLORS:
|
||||||
\fB\-1 \fRDefault terminal foreground/background color
|
\fB\-1 \fRDefault terminal foreground/background color
|
||||||
@@ -596,6 +597,9 @@ Indicator for wrapped lines. The default is '↳ ' or '> ' depending on
|
|||||||
.B "\-\-no\-multi\-line"
|
.B "\-\-no\-multi\-line"
|
||||||
Disable multi-line display of items when using \fB\-\-read0\fR
|
Disable multi-line display of items when using \fB\-\-read0\fR
|
||||||
.TP
|
.TP
|
||||||
|
.B "\-\-raw"
|
||||||
|
Enable raw mode where non-matching items are also displayed in a dimmed color.
|
||||||
|
.TP
|
||||||
.B "\-\-track"
|
.B "\-\-track"
|
||||||
Make fzf track the current selection when the result list is updated.
|
Make fzf track the current selection when the result list is updated.
|
||||||
This can be useful when browsing logs using fzf with sorting disabled. It is
|
This can be useful when browsing logs using fzf with sorting disabled. It is
|
||||||
@@ -1841,9 +1845,11 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBdelete\-char\fR \fIdel\fR
|
\fBdelete\-char\fR \fIdel\fR
|
||||||
\fBdelete\-char/eof\fR \fIctrl\-d\fR (same as \fBdelete\-char\fR except aborts fzf if query is empty)
|
\fBdelete\-char/eof\fR \fIctrl\-d\fR (same as \fBdelete\-char\fR except aborts fzf if query is empty)
|
||||||
\fBdeselect\fR
|
\fBdeselect\fR
|
||||||
\fBdeselect\-all\fR (deselect all matches; to also clear non-matched selections, use \fBclear\-multi\fR)
|
\fBdeselect\-all\fR (deselect all matches; to also clear non-matching selections, use \fBclear\-multi\fR)
|
||||||
\fBdisable\-search\fR (disable search functionality)
|
\fBdisable\-search\fR (disable search functionality)
|
||||||
\fBdown\fR \fIctrl\-j ctrl\-n down\fR
|
\fBdown\fR \fIctrl\-j down\fR
|
||||||
|
\fBdown\-match\fR \fIctrl\-n\fR \fIalt\-down\fR (move to the match below the cursor)
|
||||||
|
\fBdown\-selected\fR (move to the selected item below the cursor)
|
||||||
\fBenable\-search\fR (enable search functionality)
|
\fBenable\-search\fR (enable search functionality)
|
||||||
\fBend\-of\-line\fR \fIctrl\-e end\fR
|
\fBend\-of\-line\fR \fIctrl\-e end\fR
|
||||||
\fBexclude\fR (exclude the current item from the result)
|
\fBexclude\fR (exclude the current item from the result)
|
||||||
@@ -1861,7 +1867,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBkill\-word\fR \fIalt\-d\fR
|
\fBkill\-word\fR \fIalt\-d\fR
|
||||||
\fBlast\fR (move to the last match; same as \fBpos(\-1)\fR)
|
\fBlast\fR (move to the last match; same as \fBpos(\-1)\fR)
|
||||||
\fBnext\-history\fR (\fIctrl\-n\fR on \fB\-\-history\fR)
|
\fBnext\-history\fR (\fIctrl\-n\fR on \fB\-\-history\fR)
|
||||||
\fBnext\-selected\fR (move to the next selected item)
|
\fBnext\-selected\fR (synonym to \fBdown\-selected\fR)
|
||||||
\fBpage\-down\fR \fIpgdn\fR
|
\fBpage\-down\fR \fIpgdn\fR
|
||||||
\fBpage\-up\fR \fIpgup\fR
|
\fBpage\-up\fR \fIpgup\fR
|
||||||
\fBhalf\-page\-down\fR
|
\fBhalf\-page\-down\fR
|
||||||
@@ -1874,7 +1880,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBoffset\-middle\fR (place the current item is in the middle of the screen)
|
\fBoffset\-middle\fR (place the current item is in the middle of the screen)
|
||||||
\fBpos(...)\fR (move cursor to the numeric position; negative number to count from the end)
|
\fBpos(...)\fR (move cursor to the numeric position; negative number to count from the end)
|
||||||
\fBprev\-history\fR (\fIctrl\-p\fR on \fB\-\-history\fR)
|
\fBprev\-history\fR (\fIctrl\-p\fR on \fB\-\-history\fR)
|
||||||
\fBprev\-selected\fR (move to the previous selected item)
|
\fBprev\-selected\fR (synonym to \fBup\-selected\fR)
|
||||||
\fBpreview(...)\fR (see below for the details)
|
\fBpreview(...)\fR (see below for the details)
|
||||||
\fBpreview\-down\fR \fIshift\-down\fR
|
\fBpreview\-down\fR \fIshift\-down\fR
|
||||||
\fBpreview\-up\fR \fIshift\-up\fR
|
\fBpreview\-up\fR \fIshift\-up\fR
|
||||||
@@ -1909,6 +1915,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBtoggle\-multi\-line\fR
|
\fBtoggle\-multi\-line\fR
|
||||||
\fBtoggle\-preview\fR
|
\fBtoggle\-preview\fR
|
||||||
\fBtoggle\-preview\-wrap\fR
|
\fBtoggle\-preview\-wrap\fR
|
||||||
|
\fBtoggle\-raw\fR
|
||||||
\fBtoggle\-search\fR (toggle search functionality)
|
\fBtoggle\-search\fR (toggle search functionality)
|
||||||
\fBtoggle\-sort\fR
|
\fBtoggle\-sort\fR
|
||||||
\fBtoggle\-track\fR (toggle global tracking option (\fB\-\-track\fR))
|
\fBtoggle\-track\fR (toggle global tracking option (\fB\-\-track\fR))
|
||||||
@@ -1935,7 +1942,9 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBunix\-line\-discard\fR \fIctrl\-u\fR
|
\fBunix\-line\-discard\fR \fIctrl\-u\fR
|
||||||
\fBunix\-word\-rubout\fR \fIctrl\-w\fR
|
\fBunix\-word\-rubout\fR \fIctrl\-w\fR
|
||||||
\fBuntrack\-current\fR (stop tracking the current item; no-op if global tracking is enabled)
|
\fBuntrack\-current\fR (stop tracking the current item; no-op if global tracking is enabled)
|
||||||
\fBup\fR \fIctrl\-k ctrl\-p up\fR
|
\fBup\fR \fIctrl\-k up\fR
|
||||||
|
\fBup\-match\fR \fIctrl\-p\fR \fIalt\-up\fR (move to the match above the cursor)
|
||||||
|
\fBup\-selected\fR (move to the selected item above the cursor)
|
||||||
\fByank\fR \fIctrl\-y\fR
|
\fByank\fR \fIctrl\-y\fR
|
||||||
|
|
||||||
Each \fBtransform*\fR action has a corresponding \fBbg\-transform*\fR
|
Each \fBtransform*\fR action has a corresponding \fBbg\-transform*\fR
|
||||||
|
|||||||
@@ -77,106 +77,109 @@ func _() {
|
|||||||
_ = x[actToggleWrap-66]
|
_ = x[actToggleWrap-66]
|
||||||
_ = x[actToggleMultiLine-67]
|
_ = x[actToggleMultiLine-67]
|
||||||
_ = x[actToggleHscroll-68]
|
_ = x[actToggleHscroll-68]
|
||||||
_ = x[actTrackCurrent-69]
|
_ = x[actToggleRaw-69]
|
||||||
_ = x[actToggleInput-70]
|
_ = x[actTrackCurrent-70]
|
||||||
_ = x[actHideInput-71]
|
_ = x[actToggleInput-71]
|
||||||
_ = x[actShowInput-72]
|
_ = x[actHideInput-72]
|
||||||
_ = x[actUntrackCurrent-73]
|
_ = x[actShowInput-73]
|
||||||
_ = x[actDown-74]
|
_ = x[actUntrackCurrent-74]
|
||||||
_ = x[actUp-75]
|
_ = x[actDown-75]
|
||||||
_ = x[actPageUp-76]
|
_ = x[actDownMatch-76]
|
||||||
_ = x[actPageDown-77]
|
_ = x[actUp-77]
|
||||||
_ = x[actPosition-78]
|
_ = x[actUpMatch-78]
|
||||||
_ = x[actHalfPageUp-79]
|
_ = x[actPageUp-79]
|
||||||
_ = x[actHalfPageDown-80]
|
_ = x[actPageDown-80]
|
||||||
_ = x[actOffsetUp-81]
|
_ = x[actPosition-81]
|
||||||
_ = x[actOffsetDown-82]
|
_ = x[actHalfPageUp-82]
|
||||||
_ = x[actOffsetMiddle-83]
|
_ = x[actHalfPageDown-83]
|
||||||
_ = x[actJump-84]
|
_ = x[actOffsetUp-84]
|
||||||
_ = x[actJumpAccept-85]
|
_ = x[actOffsetDown-85]
|
||||||
_ = x[actPrintQuery-86]
|
_ = x[actOffsetMiddle-86]
|
||||||
_ = x[actRefreshPreview-87]
|
_ = x[actJump-87]
|
||||||
_ = x[actReplaceQuery-88]
|
_ = x[actJumpAccept-88]
|
||||||
_ = x[actToggleSort-89]
|
_ = x[actPrintQuery-89]
|
||||||
_ = x[actShowPreview-90]
|
_ = x[actRefreshPreview-90]
|
||||||
_ = x[actHidePreview-91]
|
_ = x[actReplaceQuery-91]
|
||||||
_ = x[actTogglePreview-92]
|
_ = x[actToggleSort-92]
|
||||||
_ = x[actTogglePreviewWrap-93]
|
_ = x[actShowPreview-93]
|
||||||
_ = x[actTransform-94]
|
_ = x[actHidePreview-94]
|
||||||
_ = x[actTransformBorderLabel-95]
|
_ = x[actTogglePreview-95]
|
||||||
_ = x[actTransformGhost-96]
|
_ = x[actTogglePreviewWrap-96]
|
||||||
_ = x[actTransformHeader-97]
|
_ = x[actTransform-97]
|
||||||
_ = x[actTransformFooter-98]
|
_ = x[actTransformBorderLabel-98]
|
||||||
_ = x[actTransformHeaderLabel-99]
|
_ = x[actTransformGhost-99]
|
||||||
_ = x[actTransformFooterLabel-100]
|
_ = x[actTransformHeader-100]
|
||||||
_ = x[actTransformInputLabel-101]
|
_ = x[actTransformFooter-101]
|
||||||
_ = x[actTransformListLabel-102]
|
_ = x[actTransformHeaderLabel-102]
|
||||||
_ = x[actTransformNth-103]
|
_ = x[actTransformFooterLabel-103]
|
||||||
_ = x[actTransformPointer-104]
|
_ = x[actTransformInputLabel-104]
|
||||||
_ = x[actTransformPreviewLabel-105]
|
_ = x[actTransformListLabel-105]
|
||||||
_ = x[actTransformPrompt-106]
|
_ = x[actTransformNth-106]
|
||||||
_ = x[actTransformQuery-107]
|
_ = x[actTransformPointer-107]
|
||||||
_ = x[actTransformSearch-108]
|
_ = x[actTransformPreviewLabel-108]
|
||||||
_ = x[actTrigger-109]
|
_ = x[actTransformPrompt-109]
|
||||||
_ = x[actBgTransform-110]
|
_ = x[actTransformQuery-110]
|
||||||
_ = x[actBgTransformBorderLabel-111]
|
_ = x[actTransformSearch-111]
|
||||||
_ = x[actBgTransformGhost-112]
|
_ = x[actTrigger-112]
|
||||||
_ = x[actBgTransformHeader-113]
|
_ = x[actBgTransform-113]
|
||||||
_ = x[actBgTransformFooter-114]
|
_ = x[actBgTransformBorderLabel-114]
|
||||||
_ = x[actBgTransformHeaderLabel-115]
|
_ = x[actBgTransformGhost-115]
|
||||||
_ = x[actBgTransformFooterLabel-116]
|
_ = x[actBgTransformHeader-116]
|
||||||
_ = x[actBgTransformInputLabel-117]
|
_ = x[actBgTransformFooter-117]
|
||||||
_ = x[actBgTransformListLabel-118]
|
_ = x[actBgTransformHeaderLabel-118]
|
||||||
_ = x[actBgTransformNth-119]
|
_ = x[actBgTransformFooterLabel-119]
|
||||||
_ = x[actBgTransformPointer-120]
|
_ = x[actBgTransformInputLabel-120]
|
||||||
_ = x[actBgTransformPreviewLabel-121]
|
_ = x[actBgTransformListLabel-121]
|
||||||
_ = x[actBgTransformPrompt-122]
|
_ = x[actBgTransformNth-122]
|
||||||
_ = x[actBgTransformQuery-123]
|
_ = x[actBgTransformPointer-123]
|
||||||
_ = x[actBgTransformSearch-124]
|
_ = x[actBgTransformPreviewLabel-124]
|
||||||
_ = x[actBgCancel-125]
|
_ = x[actBgTransformPrompt-125]
|
||||||
_ = x[actSearch-126]
|
_ = x[actBgTransformQuery-126]
|
||||||
_ = x[actPreview-127]
|
_ = x[actBgTransformSearch-127]
|
||||||
_ = x[actPreviewTop-128]
|
_ = x[actBgCancel-128]
|
||||||
_ = x[actPreviewBottom-129]
|
_ = x[actSearch-129]
|
||||||
_ = x[actPreviewUp-130]
|
_ = x[actPreview-130]
|
||||||
_ = x[actPreviewDown-131]
|
_ = x[actPreviewTop-131]
|
||||||
_ = x[actPreviewPageUp-132]
|
_ = x[actPreviewBottom-132]
|
||||||
_ = x[actPreviewPageDown-133]
|
_ = x[actPreviewUp-133]
|
||||||
_ = x[actPreviewHalfPageUp-134]
|
_ = x[actPreviewDown-134]
|
||||||
_ = x[actPreviewHalfPageDown-135]
|
_ = x[actPreviewPageUp-135]
|
||||||
_ = x[actPrevHistory-136]
|
_ = x[actPreviewPageDown-136]
|
||||||
_ = x[actPrevSelected-137]
|
_ = x[actPreviewHalfPageUp-137]
|
||||||
_ = x[actPrint-138]
|
_ = x[actPreviewHalfPageDown-138]
|
||||||
_ = x[actPut-139]
|
_ = x[actPrevHistory-139]
|
||||||
_ = x[actNextHistory-140]
|
_ = x[actPrevSelected-140]
|
||||||
_ = x[actNextSelected-141]
|
_ = x[actPrint-141]
|
||||||
_ = x[actExecute-142]
|
_ = x[actPut-142]
|
||||||
_ = x[actExecuteSilent-143]
|
_ = x[actNextHistory-143]
|
||||||
_ = x[actExecuteMulti-144]
|
_ = x[actNextSelected-144]
|
||||||
_ = x[actSigStop-145]
|
_ = x[actExecute-145]
|
||||||
_ = x[actFirst-146]
|
_ = x[actExecuteSilent-146]
|
||||||
_ = x[actLast-147]
|
_ = x[actExecuteMulti-147]
|
||||||
_ = x[actReload-148]
|
_ = x[actSigStop-148]
|
||||||
_ = x[actReloadSync-149]
|
_ = x[actFirst-149]
|
||||||
_ = x[actDisableSearch-150]
|
_ = x[actLast-150]
|
||||||
_ = x[actEnableSearch-151]
|
_ = x[actReload-151]
|
||||||
_ = x[actSelect-152]
|
_ = x[actReloadSync-152]
|
||||||
_ = x[actDeselect-153]
|
_ = x[actDisableSearch-153]
|
||||||
_ = x[actUnbind-154]
|
_ = x[actEnableSearch-154]
|
||||||
_ = x[actRebind-155]
|
_ = x[actSelect-155]
|
||||||
_ = x[actToggleBind-156]
|
_ = x[actDeselect-156]
|
||||||
_ = x[actBecome-157]
|
_ = x[actUnbind-157]
|
||||||
_ = x[actShowHeader-158]
|
_ = x[actRebind-158]
|
||||||
_ = x[actHideHeader-159]
|
_ = x[actToggleBind-159]
|
||||||
_ = x[actBell-160]
|
_ = x[actBecome-160]
|
||||||
_ = x[actExclude-161]
|
_ = x[actShowHeader-161]
|
||||||
_ = x[actExcludeMulti-162]
|
_ = x[actHideHeader-162]
|
||||||
_ = x[actAsync-163]
|
_ = x[actBell-163]
|
||||||
|
_ = x[actExclude-164]
|
||||||
|
_ = x[actExcludeMulti-165]
|
||||||
|
_ = x[actAsync-166]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactToggleRawactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactDownMatchactUpactUpMatchactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
|
||||||
|
|
||||||
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, 993, 1009, 1024, 1038, 1050, 1062, 1079, 1086, 1091, 1100, 1111, 1122, 1135, 1150, 1161, 1174, 1189, 1196, 1209, 1222, 1239, 1254, 1267, 1281, 1295, 1311, 1331, 1343, 1366, 1383, 1401, 1419, 1442, 1465, 1487, 1508, 1523, 1542, 1566, 1584, 1601, 1619, 1629, 1643, 1668, 1687, 1707, 1727, 1752, 1777, 1801, 1824, 1841, 1862, 1888, 1908, 1927, 1947, 1958, 1967, 1977, 1990, 2006, 2018, 2032, 2048, 2066, 2086, 2108, 2122, 2137, 2145, 2151, 2165, 2180, 2190, 2206, 2221, 2231, 2239, 2246, 2255, 2268, 2284, 2299, 2308, 2319, 2328, 2337, 2350, 2359, 2372, 2385, 2392, 2402, 2417, 2425}
|
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, 993, 1009, 1021, 1036, 1050, 1062, 1074, 1091, 1098, 1110, 1115, 1125, 1134, 1145, 1156, 1169, 1184, 1195, 1208, 1223, 1230, 1243, 1256, 1273, 1288, 1301, 1315, 1329, 1345, 1365, 1377, 1400, 1417, 1435, 1453, 1476, 1499, 1521, 1542, 1557, 1576, 1600, 1618, 1635, 1653, 1663, 1677, 1702, 1721, 1741, 1761, 1786, 1811, 1835, 1858, 1875, 1896, 1922, 1942, 1961, 1981, 1992, 2001, 2011, 2024, 2040, 2052, 2066, 2082, 2100, 2120, 2142, 2156, 2171, 2179, 2185, 2199, 2214, 2224, 2240, 2255, 2265, 2273, 2280, 2289, 2302, 2318, 2333, 2342, 2353, 2362, 2371, 2384, 2393, 2406, 2419, 2426, 2436, 2451, 2459}
|
||||||
|
|
||||||
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) {
|
||||||
|
|||||||
19
src/core.go
19
src/core.go
@@ -267,11 +267,11 @@ func Run(opts *Options) (int, error) {
|
|||||||
|
|
||||||
// NOTE: Streaming filter is inherently not compatible with --tail
|
// NOTE: Streaming filter is inherently not compatible with --tail
|
||||||
snapshot, _, _ := chunkList.Snapshot(opts.Tail)
|
snapshot, _, _ := chunkList.Snapshot(opts.Tail)
|
||||||
merger, _ := matcher.scan(MatchRequest{
|
result := matcher.scan(MatchRequest{
|
||||||
chunks: snapshot,
|
chunks: snapshot,
|
||||||
pattern: pattern})
|
pattern: pattern})
|
||||||
for i := 0; i < merger.Length(); i++ {
|
for i := 0; i < result.merger.Length(); i++ {
|
||||||
opts.Printer(merger.Get(i).item.AsString(opts.Ansi))
|
opts.Printer(result.merger.Get(i).item.AsString(opts.Ansi))
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,12 +479,13 @@ func Run(opts *Options) (int, error) {
|
|||||||
|
|
||||||
case EvtSearchFin:
|
case EvtSearchFin:
|
||||||
switch val := value.(type) {
|
switch val := value.(type) {
|
||||||
case *Merger:
|
case MatchResult:
|
||||||
|
merger := val.merger
|
||||||
if deferred {
|
if deferred {
|
||||||
count := val.Length()
|
count := merger.Length()
|
||||||
if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
|
if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
|
||||||
determine(val.final)
|
determine(merger.final)
|
||||||
} else if val.final {
|
} else if merger.final {
|
||||||
if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
|
if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
|
||||||
if opts.PrintQuery {
|
if opts.PrintQuery {
|
||||||
opts.Printer(opts.Query)
|
opts.Printer(opts.Query)
|
||||||
@@ -502,7 +503,7 @@ func Run(opts *Options) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
opts.Printer(transformer(val.Get(i).item))
|
opts.Printer(transformer(merger.Get(i).item))
|
||||||
}
|
}
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
exitCode = ExitNoMatch
|
exitCode = ExitNoMatch
|
||||||
@@ -510,7 +511,7 @@ func Run(opts *Options) (int, error) {
|
|||||||
stop = true
|
stop = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
determine(val.final)
|
determine(merger.final)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
terminal.UpdateList(val)
|
terminal.UpdateList(val)
|
||||||
|
|||||||
@@ -19,6 +19,20 @@ type MatchRequest struct {
|
|||||||
revision revision
|
revision revision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MatchResult struct {
|
||||||
|
merger *Merger
|
||||||
|
passMerger *Merger
|
||||||
|
cancelled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mr MatchResult) cacheable() bool {
|
||||||
|
return mr.merger != nil && mr.merger.cacheable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mr MatchResult) final() bool {
|
||||||
|
return mr.merger != nil && mr.merger.final
|
||||||
|
}
|
||||||
|
|
||||||
// Matcher is responsible for performing search
|
// Matcher is responsible for performing search
|
||||||
type Matcher struct {
|
type Matcher struct {
|
||||||
cache *ChunkCache
|
cache *ChunkCache
|
||||||
@@ -29,7 +43,7 @@ type Matcher struct {
|
|||||||
reqBox *util.EventBox
|
reqBox *util.EventBox
|
||||||
partitions int
|
partitions int
|
||||||
slab []*util.Slab
|
slab []*util.Slab
|
||||||
mergerCache map[string]*Merger
|
mergerCache map[string]MatchResult
|
||||||
revision revision
|
revision revision
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +65,7 @@ func NewMatcher(cache *ChunkCache, patternBuilder func([]rune) *Pattern,
|
|||||||
reqBox: util.NewEventBox(),
|
reqBox: util.NewEventBox(),
|
||||||
partitions: partitions,
|
partitions: partitions,
|
||||||
slab: make([]*util.Slab, partitions),
|
slab: make([]*util.Slab, partitions),
|
||||||
mergerCache: make(map[string]*Merger),
|
mergerCache: make(map[string]MatchResult),
|
||||||
revision: revision}
|
revision: revision}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +99,7 @@ func (m *Matcher) Loop() {
|
|||||||
cacheCleared := false
|
cacheCleared := false
|
||||||
if request.sort != m.sort || request.revision != m.revision {
|
if request.sort != m.sort || request.revision != m.revision {
|
||||||
m.sort = request.sort
|
m.sort = request.sort
|
||||||
m.mergerCache = make(map[string]*Merger)
|
m.mergerCache = make(map[string]MatchResult)
|
||||||
if !request.revision.compatible(m.revision) {
|
if !request.revision.compatible(m.revision) {
|
||||||
m.cache.Clear()
|
m.cache.Clear()
|
||||||
}
|
}
|
||||||
@@ -95,33 +109,32 @@ func (m *Matcher) Loop() {
|
|||||||
|
|
||||||
// Restart search
|
// Restart search
|
||||||
patternString := request.pattern.AsString()
|
patternString := request.pattern.AsString()
|
||||||
var merger *Merger
|
var result MatchResult
|
||||||
cancelled := false
|
|
||||||
count := CountItems(request.chunks)
|
count := CountItems(request.chunks)
|
||||||
|
|
||||||
if !cacheCleared {
|
if !cacheCleared {
|
||||||
if count == prevCount {
|
if count == prevCount {
|
||||||
// Look up mergerCache
|
// Look up mergerCache
|
||||||
if cached, found := m.mergerCache[patternString]; found && cached.final == request.final {
|
if cached, found := m.mergerCache[patternString]; found && cached.final() == request.final {
|
||||||
merger = cached
|
result = cached
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Invalidate mergerCache
|
// Invalidate mergerCache
|
||||||
prevCount = count
|
prevCount = count
|
||||||
m.mergerCache = make(map[string]*Merger)
|
m.mergerCache = make(map[string]MatchResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if merger == nil {
|
if result.merger == nil {
|
||||||
merger, cancelled = m.scan(request)
|
result = m.scan(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cancelled {
|
if !result.cancelled {
|
||||||
if merger.cacheable() {
|
if result.cacheable() {
|
||||||
m.mergerCache[patternString] = merger
|
m.mergerCache[patternString] = result
|
||||||
}
|
}
|
||||||
merger.final = request.final
|
result.merger.final = request.final
|
||||||
m.eventBox.Set(EvtSearchFin, merger)
|
m.eventBox.Set(EvtSearchFin, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,16 +165,18 @@ type partialResult struct {
|
|||||||
matches []Result
|
matches []Result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
func (m *Matcher) scan(request MatchRequest) MatchResult {
|
||||||
startedAt := time.Now()
|
startedAt := time.Now()
|
||||||
|
|
||||||
numChunks := len(request.chunks)
|
numChunks := len(request.chunks)
|
||||||
if numChunks == 0 {
|
if numChunks == 0 {
|
||||||
return EmptyMerger(request.revision), false
|
m := EmptyMerger(request.revision)
|
||||||
|
return MatchResult{m, m, false}
|
||||||
}
|
}
|
||||||
pattern := request.pattern
|
pattern := request.pattern
|
||||||
|
passMerger := PassMerger(&request.chunks, m.tac, request.revision)
|
||||||
if pattern.IsEmpty() {
|
if pattern.IsEmpty() {
|
||||||
return PassMerger(&request.chunks, m.tac, request.revision), false
|
return MatchResult{passMerger, passMerger, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
minIndex := request.chunks[0].items[0].Index()
|
minIndex := request.chunks[0].items[0].Index()
|
||||||
@@ -224,7 +239,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.reqBox.Peek(reqReset) {
|
if m.reqBox.Peek(reqReset) {
|
||||||
return nil, wait()
|
return MatchResult{nil, nil, wait()}
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(startedAt) > progressMinDuration {
|
if time.Since(startedAt) > progressMinDuration {
|
||||||
@@ -237,7 +252,8 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
partialResult := <-resultChan
|
partialResult := <-resultChan
|
||||||
partialResults[partialResult.index] = partialResult.matches
|
partialResults[partialResult.index] = partialResult.matches
|
||||||
}
|
}
|
||||||
return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex), false
|
merger := NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex)
|
||||||
|
return MatchResult{merger, passMerger, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset is called to interrupt/signal the ongoing search
|
// Reset is called to interrupt/signal the ongoing search
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ func (mg *Merger) Get(idx int) Result {
|
|||||||
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
|
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mg *Merger) ToMap() map[int32]Result {
|
||||||
|
ret := make(map[int32]Result, mg.count)
|
||||||
|
for i := 0; i < mg.count; i++ {
|
||||||
|
result := mg.Get(i)
|
||||||
|
ret[result.Index()] = result
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (mg *Merger) cacheable() bool {
|
func (mg *Merger) cacheable() bool {
|
||||||
return mg.count < mergerCacheMax
|
return mg.count < mergerCacheMax
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ Usage: fzf [options]
|
|||||||
--wrap Enable line wrap
|
--wrap Enable line wrap
|
||||||
--wrap-sign=STR Indicator for wrapped lines
|
--wrap-sign=STR Indicator for wrapped lines
|
||||||
--no-multi-line Disable multi-line display of items when using --read0
|
--no-multi-line Disable multi-line display of items when using --read0
|
||||||
|
--raw Enable raw mode (show non-matching items)
|
||||||
--track Track the current selection when the result is updated
|
--track Track the current selection when the result is updated
|
||||||
--tac Reverse the order of the input
|
--tac Reverse the order of the input
|
||||||
--gap[=N] Render empty lines between each item
|
--gap[=N] Render empty lines between each item
|
||||||
@@ -111,6 +112,7 @@ Usage: fzf [options]
|
|||||||
highlighted substring (default: 10)
|
highlighted substring (default: 10)
|
||||||
--jump-labels=CHARS Label characters for jump mode
|
--jump-labels=CHARS Label characters for jump mode
|
||||||
--gutter=CHAR Character used for the gutter column (default: '▌')
|
--gutter=CHAR Character used for the gutter column (default: '▌')
|
||||||
|
--gutter-raw=CHAR Character used for the gutter column in raw mode (default: '▖')
|
||||||
--pointer=STR Pointer to the current line (default: '▌' or '>')
|
--pointer=STR Pointer to the current line (default: '▌' or '>')
|
||||||
--marker=STR Multi-select marker (default: '┃' or '>')
|
--marker=STR Multi-select marker (default: '┃' or '>')
|
||||||
--marker-multi-line=STR Multi-select marker for multi-line entries;
|
--marker-multi-line=STR Multi-select marker for multi-line entries;
|
||||||
@@ -562,6 +564,7 @@ type Options struct {
|
|||||||
AcceptNth func(Delimiter) func([]Token, int32) string
|
AcceptNth func(Delimiter) func([]Token, int32) string
|
||||||
Delimiter Delimiter
|
Delimiter Delimiter
|
||||||
Sort int
|
Sort int
|
||||||
|
Raw bool
|
||||||
Track trackOption
|
Track trackOption
|
||||||
Tac bool
|
Tac bool
|
||||||
Tail int
|
Tail int
|
||||||
@@ -593,6 +596,7 @@ type Options struct {
|
|||||||
JumpLabels string
|
JumpLabels string
|
||||||
Prompt string
|
Prompt string
|
||||||
Gutter *string
|
Gutter *string
|
||||||
|
GutterRaw *string
|
||||||
Pointer *string
|
Pointer *string
|
||||||
Marker *string
|
Marker *string
|
||||||
MarkerMulti *[3]string
|
MarkerMulti *[3]string
|
||||||
@@ -714,6 +718,7 @@ func defaultOptions() *Options {
|
|||||||
JumpLabels: defaultJumpLabels,
|
JumpLabels: defaultJumpLabels,
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
Gutter: nil,
|
Gutter: nil,
|
||||||
|
GutterRaw: nil,
|
||||||
Pointer: nil,
|
Pointer: nil,
|
||||||
Marker: nil,
|
Marker: nil,
|
||||||
MarkerMulti: nil,
|
MarkerMulti: nil,
|
||||||
@@ -1442,6 +1447,8 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
mergeAttr(&theme.SelectedBg)
|
mergeAttr(&theme.SelectedBg)
|
||||||
case "nth":
|
case "nth":
|
||||||
mergeAttr(&theme.Nth)
|
mergeAttr(&theme.Nth)
|
||||||
|
case "hidden":
|
||||||
|
mergeAttr(&theme.Hidden)
|
||||||
case "gutter":
|
case "gutter":
|
||||||
mergeAttr(&theme.Gutter)
|
mergeAttr(&theme.Gutter)
|
||||||
case "hl":
|
case "hl":
|
||||||
@@ -1741,6 +1748,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actToggleMultiLine)
|
appendAction(actToggleMultiLine)
|
||||||
case "toggle-hscroll":
|
case "toggle-hscroll":
|
||||||
appendAction(actToggleHscroll)
|
appendAction(actToggleHscroll)
|
||||||
|
case "toggle-raw":
|
||||||
|
appendAction(actToggleRaw)
|
||||||
case "show-header":
|
case "show-header":
|
||||||
appendAction(actShowHeader)
|
appendAction(actShowHeader)
|
||||||
case "hide-header":
|
case "hide-header":
|
||||||
@@ -1761,8 +1770,12 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actToggle)
|
appendAction(actToggle)
|
||||||
case "down":
|
case "down":
|
||||||
appendAction(actDown)
|
appendAction(actDown)
|
||||||
|
case "down-match":
|
||||||
|
appendAction(actDownMatch)
|
||||||
case "up":
|
case "up":
|
||||||
appendAction(actUp)
|
appendAction(actUp)
|
||||||
|
case "up-match":
|
||||||
|
appendAction(actUpMatch)
|
||||||
case "first", "top":
|
case "first", "top":
|
||||||
appendAction(actFirst)
|
appendAction(actFirst)
|
||||||
case "last":
|
case "last":
|
||||||
@@ -1779,9 +1792,9 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actPrevHistory)
|
appendAction(actPrevHistory)
|
||||||
case "next-history":
|
case "next-history":
|
||||||
appendAction(actNextHistory)
|
appendAction(actNextHistory)
|
||||||
case "prev-selected":
|
case "up-selected", "prev-selected":
|
||||||
appendAction(actPrevSelected)
|
appendAction(actPrevSelected)
|
||||||
case "next-selected":
|
case "down-selected", "next-selected":
|
||||||
appendAction(actNextSelected)
|
appendAction(actNextSelected)
|
||||||
case "show-preview":
|
case "show-preview":
|
||||||
appendAction(actShowPreview)
|
appendAction(actShowPreview)
|
||||||
@@ -2682,6 +2695,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
}
|
}
|
||||||
case "+s", "--no-sort":
|
case "+s", "--no-sort":
|
||||||
opts.Sort = 0
|
opts.Sort = 0
|
||||||
|
case "--raw":
|
||||||
|
opts.Raw = true
|
||||||
|
case "--no-raw":
|
||||||
|
opts.Raw = false
|
||||||
case "--track":
|
case "--track":
|
||||||
opts.Track = trackEnabled
|
opts.Track = trackEnabled
|
||||||
case "--no-track":
|
case "--no-track":
|
||||||
@@ -2866,6 +2883,13 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
}
|
}
|
||||||
str = firstLine(str)
|
str = firstLine(str)
|
||||||
opts.Gutter = &str
|
opts.Gutter = &str
|
||||||
|
case "--gutter-raw":
|
||||||
|
str, err := nextString("gutter character for raw mode required")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
str = firstLine(str)
|
||||||
|
opts.GutterRaw = &str
|
||||||
case "--pointer":
|
case "--pointer":
|
||||||
str, err := nextString("pointer sign required")
|
str, err := nextString("pointer sign required")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3390,6 +3414,12 @@ func validateOptions(opts *Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.GutterRaw != nil {
|
||||||
|
if err := validateSign(*opts.GutterRaw, "gutter", 1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if opts.Scrollbar != nil {
|
if opts.Scrollbar != nil {
|
||||||
runes := []rune(*opts.Scrollbar)
|
runes := []rune(*opts.Scrollbar)
|
||||||
if len(runes) > 2 {
|
if len(runes) > 2 {
|
||||||
|
|||||||
@@ -350,8 +350,8 @@ func TestDefaultCtrlNP(t *testing.T) {
|
|||||||
t.Error()
|
t.Error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check([]string{}, tui.CtrlN, actDown)
|
check([]string{}, tui.CtrlN, actDownMatch)
|
||||||
check([]string{}, tui.CtrlP, actUp)
|
check([]string{}, tui.CtrlP, actUpMatch)
|
||||||
|
|
||||||
check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
|
check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
|
||||||
check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
|
check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
|
||||||
|
|||||||
221
src/terminal.go
221
src/terminal.go
@@ -274,9 +274,11 @@ type Terminal struct {
|
|||||||
footerLabelLen int
|
footerLabelLen int
|
||||||
footerLabelOpts labelOpts
|
footerLabelOpts labelOpts
|
||||||
gutterReverse bool
|
gutterReverse bool
|
||||||
|
gutterRawReverse bool
|
||||||
pointer string
|
pointer string
|
||||||
pointerLen int
|
pointerLen int
|
||||||
pointerEmpty string
|
pointerEmpty string
|
||||||
|
pointerEmptyRaw string
|
||||||
marker string
|
marker string
|
||||||
markerLen int
|
markerLen int
|
||||||
markerEmpty string
|
markerEmpty string
|
||||||
@@ -297,6 +299,7 @@ type Terminal struct {
|
|||||||
subWordNext string
|
subWordNext string
|
||||||
cx int
|
cx int
|
||||||
cy int
|
cy int
|
||||||
|
lastMatchingIndex int32
|
||||||
offset int
|
offset int
|
||||||
xoffset int
|
xoffset int
|
||||||
yanked []rune
|
yanked []rune
|
||||||
@@ -383,6 +386,9 @@ type Terminal struct {
|
|||||||
printer func(string)
|
printer func(string)
|
||||||
printsep string
|
printsep string
|
||||||
merger *Merger
|
merger *Merger
|
||||||
|
passMerger *Merger
|
||||||
|
resultMerger *Merger
|
||||||
|
matchMap map[int32]Result
|
||||||
selected map[int32]selectedItem
|
selected map[int32]selectedItem
|
||||||
version int64
|
version int64
|
||||||
revision revision
|
revision revision
|
||||||
@@ -429,6 +435,7 @@ type Terminal struct {
|
|||||||
clickFooterColumn int
|
clickFooterColumn int
|
||||||
proxyScript string
|
proxyScript string
|
||||||
numLinesCache map[int32]numLinesCacheValue
|
numLinesCache map[int32]numLinesCacheValue
|
||||||
|
raw bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type numLinesCacheValue struct {
|
type numLinesCacheValue struct {
|
||||||
@@ -569,13 +576,16 @@ const (
|
|||||||
actToggleWrap
|
actToggleWrap
|
||||||
actToggleMultiLine
|
actToggleMultiLine
|
||||||
actToggleHscroll
|
actToggleHscroll
|
||||||
|
actToggleRaw
|
||||||
actTrackCurrent
|
actTrackCurrent
|
||||||
actToggleInput
|
actToggleInput
|
||||||
actHideInput
|
actHideInput
|
||||||
actShowInput
|
actShowInput
|
||||||
actUntrackCurrent
|
actUntrackCurrent
|
||||||
actDown
|
actDown
|
||||||
|
actDownMatch
|
||||||
actUp
|
actUp
|
||||||
|
actUpMatch
|
||||||
actPageUp
|
actPageUp
|
||||||
actPageDown
|
actPageDown
|
||||||
actPosition
|
actPosition
|
||||||
@@ -796,8 +806,10 @@ func defaultKeymap() map[tui.Event][]*action {
|
|||||||
add(tui.CtrlK, actUp)
|
add(tui.CtrlK, actUp)
|
||||||
add(tui.CtrlL, actClearScreen)
|
add(tui.CtrlL, actClearScreen)
|
||||||
add(tui.Enter, actAccept)
|
add(tui.Enter, actAccept)
|
||||||
add(tui.CtrlN, actDown)
|
add(tui.CtrlN, actDownMatch)
|
||||||
add(tui.CtrlP, actUp)
|
add(tui.CtrlP, actUpMatch)
|
||||||
|
add(tui.AltDown, actDownMatch)
|
||||||
|
add(tui.AltUp, actUpMatch)
|
||||||
add(tui.CtrlU, actUnixLineDiscard)
|
add(tui.CtrlU, actUnixLineDiscard)
|
||||||
add(tui.CtrlW, actUnixWordRubout)
|
add(tui.CtrlW, actUnixWordRubout)
|
||||||
add(tui.CtrlY, actYank)
|
add(tui.CtrlY, actYank)
|
||||||
@@ -953,6 +965,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
}
|
}
|
||||||
keymapCopy := maps.Clone(opts.Keymap)
|
keymapCopy := maps.Clone(opts.Keymap)
|
||||||
|
|
||||||
|
em := EmptyMerger(revision{})
|
||||||
t := Terminal{
|
t := Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
infoCommand: opts.InfoCommand,
|
infoCommand: opts.InfoCommand,
|
||||||
@@ -980,6 +993,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
subWordNext: subWordNext,
|
subWordNext: subWordNext,
|
||||||
cx: len(input),
|
cx: len(input),
|
||||||
cy: 0,
|
cy: 0,
|
||||||
|
lastMatchingIndex: minItem.Index(),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
xoffset: 0,
|
xoffset: 0,
|
||||||
yanked: []rune{},
|
yanked: []rune{},
|
||||||
@@ -1039,6 +1053,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
nth: opts.Nth,
|
nth: opts.Nth,
|
||||||
nthCurrent: opts.Nth,
|
nthCurrent: opts.Nth,
|
||||||
tabstop: opts.Tabstop,
|
tabstop: opts.Tabstop,
|
||||||
|
raw: opts.Raw,
|
||||||
hasStartActions: false,
|
hasStartActions: false,
|
||||||
hasResultActions: false,
|
hasResultActions: false,
|
||||||
hasFocusActions: false,
|
hasFocusActions: false,
|
||||||
@@ -1052,7 +1067,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
printer: opts.Printer,
|
printer: opts.Printer,
|
||||||
printsep: opts.PrintSep,
|
printsep: opts.PrintSep,
|
||||||
proxyScript: opts.ProxyScript,
|
proxyScript: opts.ProxyScript,
|
||||||
merger: EmptyMerger(revision{}),
|
merger: em,
|
||||||
|
passMerger: em,
|
||||||
|
resultMerger: em,
|
||||||
|
matchMap: make(map[int32]Result),
|
||||||
selected: make(map[int32]selectedItem),
|
selected: make(map[int32]selectedItem),
|
||||||
runningCmds: util.NewConcurrentSet[*runningCmd](),
|
runningCmds: util.NewConcurrentSet[*runningCmd](),
|
||||||
reqBox: util.NewEventBox(),
|
reqBox: util.NewEventBox(),
|
||||||
@@ -1093,7 +1111,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible())
|
tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible())
|
||||||
|
|
||||||
// Gutter character
|
// Gutter character
|
||||||
var gutterChar string
|
var gutterChar, gutterRawChar string
|
||||||
if opts.Gutter != nil {
|
if opts.Gutter != nil {
|
||||||
gutterChar = *opts.Gutter
|
gutterChar = *opts.Gutter
|
||||||
} else if t.unicode && !t.theme.Gutter.Color.IsDefault() {
|
} else if t.unicode && !t.theme.Gutter.Color.IsDefault() {
|
||||||
@@ -1103,12 +1121,24 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
t.gutterReverse = true
|
t.gutterReverse = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.GutterRaw != nil {
|
||||||
|
gutterRawChar = *opts.GutterRaw
|
||||||
|
} else if t.unicode && !t.theme.Gutter.Color.IsDefault() {
|
||||||
|
// TODO: Doesn't look too good. Maybe use a different color instead, or both?
|
||||||
|
gutterRawChar = "▖"
|
||||||
|
} else {
|
||||||
|
gutterRawChar = ":"
|
||||||
|
t.gutterRawReverse = false
|
||||||
|
}
|
||||||
|
|
||||||
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
|
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
|
||||||
// Pre-calculated empty pointer and marker signs
|
// Pre-calculated empty pointer and marker signs
|
||||||
if t.pointerLen == 0 {
|
if t.pointerLen == 0 {
|
||||||
t.pointerEmpty = ""
|
t.pointerEmpty = ""
|
||||||
|
t.pointerEmptyRaw = ""
|
||||||
} else {
|
} else {
|
||||||
t.pointerEmpty = gutterChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
|
t.pointerEmpty = gutterChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
|
||||||
|
t.pointerEmptyRaw = gutterRawChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
|
||||||
}
|
}
|
||||||
t.markerEmpty = strings.Repeat(" ", t.markerLen)
|
t.markerEmpty = strings.Repeat(" ", t.markerLen)
|
||||||
|
|
||||||
@@ -1282,7 +1312,7 @@ func (t *Terminal) environImpl(forPreview bool) []string {
|
|||||||
}
|
}
|
||||||
env = append(env, "FZF_INPUT_STATE="+inputState)
|
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.resultMerger.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)))
|
||||||
env = append(env, fmt.Sprintf("FZF_LINES=%d", t.areaLines))
|
env = append(env, fmt.Sprintf("FZF_LINES=%d", t.areaLines))
|
||||||
env = append(env, fmt.Sprintf("FZF_COLUMNS=%d", t.areaColumns))
|
env = append(env, fmt.Sprintf("FZF_COLUMNS=%d", t.areaColumns))
|
||||||
@@ -1693,7 +1723,8 @@ func (t *Terminal) UpdateProgress(progress float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateList updates Merger to display the list
|
// UpdateList updates Merger to display the list
|
||||||
func (t *Terminal) UpdateList(merger *Merger) {
|
func (t *Terminal) UpdateList(result MatchResult) {
|
||||||
|
merger := result.merger
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
prevIndex := minItem.Index()
|
prevIndex := minItem.Index()
|
||||||
newRevision := merger.Revision()
|
newRevision := merger.Revision()
|
||||||
@@ -1706,6 +1737,15 @@ func (t *Terminal) UpdateList(merger *Merger) {
|
|||||||
}
|
}
|
||||||
t.progress = 100
|
t.progress = 100
|
||||||
t.merger = merger
|
t.merger = merger
|
||||||
|
t.resultMerger = merger
|
||||||
|
t.passMerger = result.passMerger
|
||||||
|
if t.raw {
|
||||||
|
t.merger = result.passMerger
|
||||||
|
t.matchMap = t.resultMerger.ToMap()
|
||||||
|
} else {
|
||||||
|
t.merger = result.merger
|
||||||
|
t.matchMap = make(map[int32]Result)
|
||||||
|
}
|
||||||
if t.revision != newRevision {
|
if t.revision != newRevision {
|
||||||
if !t.revision.compatible(newRevision) {
|
if !t.revision.compatible(newRevision) {
|
||||||
// Reloaded: clear selection
|
// Reloaded: clear selection
|
||||||
@@ -1754,7 +1794,7 @@ func (t *Terminal) UpdateList(merger *Merger) {
|
|||||||
}
|
}
|
||||||
needActivation := false
|
needActivation := false
|
||||||
if !t.reading {
|
if !t.reading {
|
||||||
switch t.merger.Length() {
|
switch t.resultMerger.Length() {
|
||||||
case 0:
|
case 0:
|
||||||
zero := tui.Zero.AsEvent()
|
zero := tui.Zero.AsEvent()
|
||||||
if _, prs := t.keymap[zero]; prs {
|
if _, prs := t.keymap[zero]; prs {
|
||||||
@@ -2801,7 +2841,7 @@ func (t *Terminal) printInfoImpl() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
found := t.merger.Length()
|
found := t.resultMerger.Length()
|
||||||
total := util.Max(found, t.count)
|
total := util.Max(found, t.count)
|
||||||
output := fmt.Sprintf("%d/%d", found, total)
|
output := fmt.Sprintf("%d/%d", found, total)
|
||||||
if t.toggleSort {
|
if t.toggleSort {
|
||||||
@@ -3122,12 +3162,16 @@ func (t *Terminal) gutter(current bool) {
|
|||||||
var color tui.ColorPair
|
var color tui.ColorPair
|
||||||
if current {
|
if current {
|
||||||
color = tui.ColCurrentCursorEmpty
|
color = tui.ColCurrentCursorEmpty
|
||||||
} else if t.gutterReverse {
|
} else if !t.raw && t.gutterReverse || t.raw && t.gutterRawReverse {
|
||||||
color = tui.ColCursorEmpty
|
color = tui.ColCursorEmpty
|
||||||
} else {
|
} else {
|
||||||
color = tui.ColCursorEmptyChar
|
color = tui.ColCursorEmptyChar
|
||||||
}
|
}
|
||||||
t.window.CPrint(color, t.pointerEmpty)
|
gutter := t.pointerEmpty
|
||||||
|
if t.raw {
|
||||||
|
gutter = t.pointerEmptyRaw
|
||||||
|
}
|
||||||
|
t.window.CPrint(color, gutter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) {
|
func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) {
|
||||||
@@ -3162,7 +3206,11 @@ func (t *Terminal) printList() {
|
|||||||
for line, itemCount := startLine, 0; line <= maxy; line, itemCount = line+1, itemCount+1 {
|
for line, itemCount := startLine, 0; line <= maxy; line, itemCount = line+1, itemCount+1 {
|
||||||
if itemCount < count {
|
if itemCount < count {
|
||||||
item := t.merger.Get(itemCount + t.offset)
|
item := t.merger.Get(itemCount + t.offset)
|
||||||
line = t.printItem(item, line, maxy, itemCount, itemCount == t.cy-t.offset, barRange)
|
current := itemCount == t.cy-t.offset
|
||||||
|
if current && (!t.raw || t.isItemMatch(item.item)) {
|
||||||
|
t.lastMatchingIndex = item.Index()
|
||||||
|
}
|
||||||
|
line = t.printItem(item, line, maxy, itemCount, current, barRange)
|
||||||
} else if !t.prevLines[line].empty {
|
} else if !t.prevLines[line].empty {
|
||||||
t.renderEmptyLine(line, barRange)
|
t.renderEmptyLine(line, barRange)
|
||||||
}
|
}
|
||||||
@@ -3184,6 +3232,14 @@ func (t *Terminal) printBar(lineNum int, forceRedraw bool, barRange [2]int) bool
|
|||||||
|
|
||||||
func (t *Terminal) printItem(result Result, line int, maxLine int, index int, current bool, barRange [2]int) int {
|
func (t *Terminal) printItem(result Result, line int, maxLine int, index int, current bool, barRange [2]int) int {
|
||||||
item := result.item
|
item := result.item
|
||||||
|
matched := true
|
||||||
|
var matchResult Result
|
||||||
|
if t.raw {
|
||||||
|
if matchResult, matched = t.matchMap[item.Index()]; matched {
|
||||||
|
result = matchResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, selected := t.selected[item.Index()]
|
_, selected := t.selected[item.Index()]
|
||||||
label := ""
|
label := ""
|
||||||
extraWidth := 0
|
extraWidth := 0
|
||||||
@@ -3310,7 +3366,13 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
|
|||||||
}
|
}
|
||||||
return indentSize
|
return indentSize
|
||||||
}
|
}
|
||||||
finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, line, maxLine, forceRedraw, preTask, postTask)
|
base := tui.ColCurrent
|
||||||
|
match := tui.ColCurrentMatch
|
||||||
|
if !matched {
|
||||||
|
base = base.WithFg(t.theme.Hidden)
|
||||||
|
match = match.WithFg(t.theme.Hidden)
|
||||||
|
}
|
||||||
|
finalLineNum = t.printHighlighted(result, base, match, true, true, line, maxLine, forceRedraw, preTask, postTask)
|
||||||
} else {
|
} else {
|
||||||
preTask := func(marker markerClass) int {
|
preTask := func(marker markerClass) int {
|
||||||
w := t.window.Width() - t.pointerLen
|
w := t.window.Width() - t.pointerLen
|
||||||
@@ -3344,6 +3406,10 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
|
|||||||
base = base.WithBg(altBg)
|
base = base.WithBg(altBg)
|
||||||
match = match.WithBg(altBg)
|
match = match.WithBg(altBg)
|
||||||
}
|
}
|
||||||
|
if !matched {
|
||||||
|
base = base.WithFg(t.theme.Hidden)
|
||||||
|
match = match.WithFg(t.theme.Hidden)
|
||||||
|
}
|
||||||
finalLineNum = t.printHighlighted(result, base, match, false, true, line, maxLine, forceRedraw, preTask, postTask)
|
finalLineNum = t.printHighlighted(result, base, match, false, true, line, maxLine, forceRedraw, preTask, postTask)
|
||||||
}
|
}
|
||||||
for i := 0; i < t.gap && finalLineNum < maxLine; i++ {
|
for i := 0; i < t.gap && finalLineNum < maxLine; i++ {
|
||||||
@@ -3396,8 +3462,8 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
|||||||
item := result.item
|
item := result.item
|
||||||
matchOffsets := []Offset{}
|
matchOffsets := []Offset{}
|
||||||
var pos *[]int
|
var pos *[]int
|
||||||
if match && t.merger.pattern != nil {
|
if match && t.resultMerger.pattern != nil {
|
||||||
_, matchOffsets, pos = t.merger.pattern.MatchItem(item, true, t.slab)
|
_, matchOffsets, pos = t.resultMerger.pattern.MatchItem(item, true, t.slab)
|
||||||
}
|
}
|
||||||
charOffsets := matchOffsets
|
charOffsets := matchOffsets
|
||||||
if pos != nil {
|
if pos != nil {
|
||||||
@@ -3429,7 +3495,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
|||||||
}
|
}
|
||||||
if !wholeCovered && t.nthAttr > 0 {
|
if !wholeCovered && t.nthAttr > 0 {
|
||||||
var tokens []Token
|
var tokens []Token
|
||||||
if item.transformed != nil && item.transformed.revision == t.merger.revision {
|
if item.transformed != nil && item.transformed.revision == t.resultMerger.revision {
|
||||||
tokens = item.transformed.tokens
|
tokens = item.transformed.tokens
|
||||||
} else {
|
} else {
|
||||||
tokens = Transform(Tokenize(item.text.ToString(), t.delimiter), t.nthCurrent)
|
tokens = Transform(Tokenize(item.text.ToString(), t.delimiter), t.nthCurrent)
|
||||||
@@ -4677,6 +4743,33 @@ func (t *Terminal) currentItem() *Item {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) isCurrentItemMatch() bool {
|
||||||
|
cnt := t.merger.Length()
|
||||||
|
if t.cy >= 0 && cnt > 0 && cnt > t.cy {
|
||||||
|
if !t.raw {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
item := t.merger.Get(t.cy).item
|
||||||
|
return t.isItemMatch(item)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) isItemMatch(item *Item) bool {
|
||||||
|
_, matched := t.matchMap[item.Index()]
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) filterSelected() {
|
||||||
|
filtered := make(map[int32]selectedItem)
|
||||||
|
for k, v := range t.selected {
|
||||||
|
if t.isItemMatch(v.item) {
|
||||||
|
filtered[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.selected = filtered
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) {
|
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) {
|
||||||
current := t.currentItem()
|
current := t.currentItem()
|
||||||
slot, plus, asterisk, forceUpdate := hasPreviewFlags(template)
|
slot, plus, asterisk, forceUpdate := hasPreviewFlags(template)
|
||||||
@@ -4721,6 +4814,10 @@ func (t *Terminal) selectItem(item *Item) bool {
|
|||||||
if len(t.selected) >= t.multi {
|
if len(t.selected) >= t.multi {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// TODO: Should we allow selecting non-matching items?
|
||||||
|
// if t.raw && !t.isItemMatch(item) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
if _, found := t.selected[item.Index()]; found {
|
if _, found := t.selected[item.Index()]; found {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -5917,8 +6014,9 @@ func (t *Terminal) Loop() error {
|
|||||||
}
|
}
|
||||||
case actSelectAll:
|
case actSelectAll:
|
||||||
if t.multi > 0 {
|
if t.multi > 0 {
|
||||||
for i := 0; i < t.merger.Length(); i++ {
|
// Limit the scope only to the matching items
|
||||||
if !t.selectItem(t.merger.Get(i).item) {
|
for i := 0; i < t.resultMerger.Length(); i++ {
|
||||||
|
if !t.selectItem(t.resultMerger.Get(i).item) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5926,8 +6024,10 @@ func (t *Terminal) Loop() error {
|
|||||||
}
|
}
|
||||||
case actDeselectAll:
|
case actDeselectAll:
|
||||||
if t.multi > 0 {
|
if t.multi > 0 {
|
||||||
for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
|
// Also limit the scope only to the matching items, while this may
|
||||||
t.deselectItem(t.merger.Get(i).item)
|
// not be straightforward in raw mode.
|
||||||
|
for i := 0; i < t.resultMerger.Length() && len(t.selected) > 0; i++ {
|
||||||
|
t.deselectItem(t.resultMerger.Get(i).item)
|
||||||
}
|
}
|
||||||
req(reqList, reqInfo)
|
req(reqList, reqInfo)
|
||||||
}
|
}
|
||||||
@@ -5955,17 +6055,17 @@ func (t *Terminal) Loop() error {
|
|||||||
case actToggleAll:
|
case actToggleAll:
|
||||||
if t.multi > 0 {
|
if t.multi > 0 {
|
||||||
prevIndexes := make(map[int]struct{})
|
prevIndexes := make(map[int]struct{})
|
||||||
for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
|
for i := 0; i < t.resultMerger.Length() && len(t.selected) > 0; i++ {
|
||||||
item := t.merger.Get(i).item
|
item := t.resultMerger.Get(i).item
|
||||||
if _, found := t.selected[item.Index()]; found {
|
if _, found := t.selected[item.Index()]; found {
|
||||||
prevIndexes[i] = struct{}{}
|
prevIndexes[i] = struct{}{}
|
||||||
t.deselectItem(item)
|
t.deselectItem(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < t.merger.Length(); i++ {
|
for i := 0; i < t.resultMerger.Length(); i++ {
|
||||||
if _, found := prevIndexes[i]; !found {
|
if _, found := prevIndexes[i]; !found {
|
||||||
item := t.merger.Get(i).item
|
item := t.resultMerger.Get(i).item
|
||||||
if !t.selectItem(item) {
|
if !t.selectItem(item) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -5993,19 +6093,77 @@ func (t *Terminal) Loop() error {
|
|||||||
t.vmove(1, true)
|
t.vmove(1, true)
|
||||||
req(reqList)
|
req(reqList)
|
||||||
}
|
}
|
||||||
case actDown:
|
case actDown, actDownMatch:
|
||||||
|
if t.raw && a.t == actDownMatch {
|
||||||
|
if t.resultMerger.Length() > 0 {
|
||||||
|
prevCy := t.cy
|
||||||
|
for t.vmove(-1, true) && !t.isCurrentItemMatch() {
|
||||||
|
}
|
||||||
|
if !t.isCurrentItemMatch() {
|
||||||
|
t.vset(prevCy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
t.vmove(-1, true)
|
t.vmove(-1, true)
|
||||||
|
}
|
||||||
req(reqList)
|
req(reqList)
|
||||||
case actUp:
|
case actUp, actUpMatch:
|
||||||
|
if t.raw && a.t == actUpMatch {
|
||||||
|
if t.resultMerger.Length() > 0 {
|
||||||
|
prevCy := t.cy
|
||||||
|
for t.vmove(1, true) && !t.isCurrentItemMatch() {
|
||||||
|
}
|
||||||
|
if !t.isCurrentItemMatch() {
|
||||||
|
t.vset(prevCy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
t.vmove(1, true)
|
t.vmove(1, true)
|
||||||
|
}
|
||||||
|
req(reqList)
|
||||||
|
case actToggleRaw:
|
||||||
|
t.raw = !t.raw
|
||||||
|
prevPos := t.cy - t.offset
|
||||||
|
prevIndex := t.currentIndex()
|
||||||
|
if t.raw {
|
||||||
|
// Build matchMap if not available
|
||||||
|
if len(t.matchMap) == 0 {
|
||||||
|
t.matchMap = t.resultMerger.ToMap()
|
||||||
|
}
|
||||||
|
t.merger = t.passMerger
|
||||||
|
} else {
|
||||||
|
t.merger = t.resultMerger
|
||||||
|
|
||||||
|
// Need to remove non-matching items from the selection
|
||||||
|
if t.multi > 0 && len(t.selected) > 0 {
|
||||||
|
t.filterSelected()
|
||||||
|
req(reqInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to retain position
|
||||||
|
if prevIndex != minItem.Index() {
|
||||||
|
i := t.merger.FindIndex(prevIndex)
|
||||||
|
if i >= 0 {
|
||||||
|
t.cy = i
|
||||||
|
} else {
|
||||||
|
t.cy = t.merger.FindIndex(t.lastMatchingIndex)
|
||||||
|
}
|
||||||
|
t.offset = t.cy - prevPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// List needs to be rerendered
|
||||||
|
t.forceRerenderList()
|
||||||
req(reqList)
|
req(reqList)
|
||||||
case actAccept:
|
case actAccept:
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
case actAcceptNonEmpty:
|
case actAcceptNonEmpty:
|
||||||
|
// TODO: Allow accepting unmatched items in raw mode?
|
||||||
if len(t.selected) > 0 || t.merger.Length() > 0 || !t.reading && t.count == 0 {
|
if len(t.selected) > 0 || t.merger.Length() > 0 || !t.reading && t.count == 0 {
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
}
|
}
|
||||||
case actAcceptOrPrintQuery:
|
case actAcceptOrPrintQuery:
|
||||||
|
// TODO: Allow accepting unmatched items in raw mode?
|
||||||
if len(t.selected) > 0 || t.merger.Length() > 0 {
|
if len(t.selected) > 0 || t.merger.Length() > 0 {
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
} else {
|
} else {
|
||||||
@@ -6841,7 +6999,7 @@ func (t *Terminal) Loop() error {
|
|||||||
reload := changed || newCommand != nil
|
reload := changed || newCommand != nil
|
||||||
var reloadRequest *searchRequest
|
var reloadRequest *searchRequest
|
||||||
if reload {
|
if reload {
|
||||||
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.merger.Revision()}
|
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.resultMerger.Revision()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch queued background requests
|
// Dispatch queued background requests
|
||||||
@@ -6961,7 +7119,8 @@ func (t *Terminal) constrain() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) vmove(o int, allowCycle bool) {
|
// Returns true if the cursor position is successfully updated
|
||||||
|
func (t *Terminal) vmove(o int, allowCycle bool) bool {
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
o *= -1
|
o *= -1
|
||||||
}
|
}
|
||||||
@@ -6978,7 +7137,7 @@ func (t *Terminal) vmove(o int, allowCycle bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.vset(dest)
|
return t.vset(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) vset(o int) bool {
|
func (t *Terminal) vset(o int) bool {
|
||||||
@@ -7045,9 +7204,9 @@ func (t *Terminal) dumpStatus(params getParams) string {
|
|||||||
selected[i] = t.dumpItem(selectedItems[i+params.offset].item)
|
selected[i] = t.dumpItem(selectedItems[i+params.offset].item)
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := make([]StatusItem, util.Max(0, util.Min(params.limit, t.merger.Length()-params.offset)))
|
matches := make([]StatusItem, util.Max(0, util.Min(params.limit, t.resultMerger.Length()-params.offset)))
|
||||||
for i := range matches {
|
for i := range matches {
|
||||||
matches[i] = t.dumpItem(t.merger.Get(i + params.offset).item)
|
matches[i] = t.dumpItem(t.resultMerger.Get(i + params.offset).item)
|
||||||
}
|
}
|
||||||
|
|
||||||
var current *StatusItem
|
var current *StatusItem
|
||||||
@@ -7064,7 +7223,7 @@ func (t *Terminal) dumpStatus(params getParams) string {
|
|||||||
Position: t.cy,
|
Position: t.cy,
|
||||||
Sort: t.sort,
|
Sort: t.sort,
|
||||||
TotalCount: t.count,
|
TotalCount: t.count,
|
||||||
MatchCount: t.merger.Length(),
|
MatchCount: t.resultMerger.Length(),
|
||||||
Current: current,
|
Current: current,
|
||||||
Matches: matches,
|
Matches: matches,
|
||||||
Selected: selected,
|
Selected: selected,
|
||||||
|
|||||||
@@ -381,6 +381,12 @@ func (p ColorPair) WithAttr(attr Attr) ColorPair {
|
|||||||
return dup
|
return dup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) WithFg(fg ColorAttr) ColorPair {
|
||||||
|
dup := p
|
||||||
|
fgPair := ColorPair{fg.Color, colUndefined, fg.Attr}
|
||||||
|
return dup.Merge(fgPair)
|
||||||
|
}
|
||||||
|
|
||||||
func (p ColorPair) WithBg(bg ColorAttr) ColorPair {
|
func (p ColorPair) WithBg(bg ColorAttr) ColorPair {
|
||||||
dup := p
|
dup := p
|
||||||
bgPair := ColorPair{colUndefined, bg.Color, bg.Attr}
|
bgPair := ColorPair{colUndefined, bg.Color, bg.Attr}
|
||||||
@@ -410,6 +416,7 @@ type ColorTheme struct {
|
|||||||
ListBg ColorAttr
|
ListBg ColorAttr
|
||||||
AltBg ColorAttr
|
AltBg ColorAttr
|
||||||
Nth ColorAttr
|
Nth ColorAttr
|
||||||
|
Hidden ColorAttr
|
||||||
SelectedFg ColorAttr
|
SelectedFg ColorAttr
|
||||||
SelectedBg ColorAttr
|
SelectedBg ColorAttr
|
||||||
SelectedMatch ColorAttr
|
SelectedMatch ColorAttr
|
||||||
@@ -866,6 +873,7 @@ func EmptyTheme() *ColorTheme {
|
|||||||
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Hidden: ColorAttr{colUndefined, Dim},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,6 +924,7 @@ func NoColorTheme() *ColorTheme {
|
|||||||
FooterLabel: ColorAttr{colDefault, AttrUndefined},
|
FooterLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
GapLine: ColorAttr{colDefault, AttrUndefined},
|
GapLine: ColorAttr{colDefault, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Hidden: ColorAttr{colUndefined, Dim},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -967,6 +976,7 @@ func init() {
|
|||||||
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Hidden: ColorAttr{colUndefined, Dim},
|
||||||
}
|
}
|
||||||
Dark256 = &ColorTheme{
|
Dark256 = &ColorTheme{
|
||||||
Colored: true,
|
Colored: true,
|
||||||
@@ -1015,6 +1025,7 @@ func init() {
|
|||||||
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Hidden: ColorAttr{colUndefined, Dim},
|
||||||
}
|
}
|
||||||
Light256 = &ColorTheme{
|
Light256 = &ColorTheme{
|
||||||
Colored: true,
|
Colored: true,
|
||||||
@@ -1063,6 +1074,7 @@ func init() {
|
|||||||
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Hidden: ColorAttr{colUndefined, Dim},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
test/test_raw.rb
Normal file
60
test/test_raw.rb
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative 'lib/common'
|
||||||
|
|
||||||
|
# Testing raw mode
|
||||||
|
class TestRaw < TestInteractive
|
||||||
|
def test_raw_mode
|
||||||
|
tmux.send_keys %(seq 1000 | #{FZF} --raw --bind ctrl-x:toggle-raw --gutter '▌' --multi), :Enter
|
||||||
|
tmux.until { assert_equal 1000, it.match_count }
|
||||||
|
|
||||||
|
tmux.send_keys 1
|
||||||
|
tmux.until { assert_equal 272, it.match_count }
|
||||||
|
|
||||||
|
tmux.send_keys :Up
|
||||||
|
tmux.until { assert_includes it, '> 2' }
|
||||||
|
|
||||||
|
tmux.send_keys 'C-p'
|
||||||
|
tmux.until do
|
||||||
|
assert_includes it, '> 10'
|
||||||
|
assert_includes it, '▖ 9'
|
||||||
|
end
|
||||||
|
|
||||||
|
tmux.send_keys 'C-x'
|
||||||
|
tmux.until do
|
||||||
|
assert_includes it, '> 10'
|
||||||
|
assert_includes it, '▌ 1'
|
||||||
|
end
|
||||||
|
|
||||||
|
tmux.send_keys :Up, 'C-x'
|
||||||
|
tmux.until do
|
||||||
|
assert_includes it, '> 11'
|
||||||
|
assert_includes it, '▖ 10'
|
||||||
|
end
|
||||||
|
|
||||||
|
tmux.send_keys 1
|
||||||
|
tmux.until { assert_equal 28, it.match_count }
|
||||||
|
|
||||||
|
tmux.send_keys 'C-p'
|
||||||
|
tmux.until do
|
||||||
|
assert_includes it, '> 101'
|
||||||
|
assert_includes it, '▖ 100'
|
||||||
|
end
|
||||||
|
|
||||||
|
tmux.send_keys 'C-n'
|
||||||
|
tmux.until do
|
||||||
|
assert_includes it, '> 11'
|
||||||
|
assert_includes it, '▖ 10'
|
||||||
|
end
|
||||||
|
|
||||||
|
tmux.send_keys :Tab, :Tab, :Tab
|
||||||
|
tmux.until { assert_equal 3, it.select_count }
|
||||||
|
|
||||||
|
tmux.send_keys 'C-x'
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 1, it.select_count
|
||||||
|
assert_includes it, '▌ 110'
|
||||||
|
assert_includes it, '>>11'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user