From d6584543e993ae74615057cb02c4c831350b75bd Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 26 Jan 2025 15:04:30 +0900 Subject: Make click-header export $FZF_CLICK_HEADER_{NTH,WORD} --- src/actiontype_string.go | 85 ++++++++++++++++++++++++------------------------ src/options.go | 4 ++- src/terminal.go | 78 +++++++++++++++++++++++++++++++++++++++++--- src/tokenizer.go | 12 +++++++ 4 files changed, 131 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/actiontype_string.go b/src/actiontype_string.go index 143e02b6..b37446c5 100644 --- a/src/actiontype_string.go +++ b/src/actiontype_string.go @@ -93,51 +93,52 @@ func _() { _ = x[actTransformInputLabel-82] _ = x[actTransformHeader-83] _ = x[actTransformHeaderLabel-84] - _ = x[actTransformPreviewLabel-85] - _ = x[actTransformPrompt-86] - _ = x[actTransformQuery-87] - _ = x[actTransformSearch-88] - _ = x[actSearch-89] - _ = x[actPreview-90] - _ = x[actChangePreview-91] - _ = x[actChangePreviewWindow-92] - _ = x[actPreviewTop-93] - _ = x[actPreviewBottom-94] - _ = x[actPreviewUp-95] - _ = x[actPreviewDown-96] - _ = x[actPreviewPageUp-97] - _ = x[actPreviewPageDown-98] - _ = x[actPreviewHalfPageUp-99] - _ = x[actPreviewHalfPageDown-100] - _ = x[actPrevHistory-101] - _ = x[actPrevSelected-102] - _ = x[actPrint-103] - _ = x[actPut-104] - _ = x[actNextHistory-105] - _ = x[actNextSelected-106] - _ = x[actExecute-107] - _ = x[actExecuteSilent-108] - _ = x[actExecuteMulti-109] - _ = x[actSigStop-110] - _ = x[actFirst-111] - _ = x[actLast-112] - _ = x[actReload-113] - _ = x[actReloadSync-114] - _ = x[actDisableSearch-115] - _ = x[actEnableSearch-116] - _ = x[actSelect-117] - _ = x[actDeselect-118] - _ = x[actUnbind-119] - _ = x[actRebind-120] - _ = x[actBecome-121] - _ = x[actShowHeader-122] - _ = x[actHideHeader-123] - _ = x[actBell-124] + _ = x[actTransformNth-85] + _ = x[actTransformPreviewLabel-86] + _ = x[actTransformPrompt-87] + _ = x[actTransformQuery-88] + _ = x[actTransformSearch-89] + _ = x[actSearch-90] + _ = x[actPreview-91] + _ = x[actChangePreview-92] + _ = x[actChangePreviewWindow-93] + _ = x[actPreviewTop-94] + _ = x[actPreviewBottom-95] + _ = x[actPreviewUp-96] + _ = x[actPreviewDown-97] + _ = x[actPreviewPageUp-98] + _ = x[actPreviewPageDown-99] + _ = x[actPreviewHalfPageUp-100] + _ = x[actPreviewHalfPageDown-101] + _ = x[actPrevHistory-102] + _ = x[actPrevSelected-103] + _ = x[actPrint-104] + _ = x[actPut-105] + _ = x[actNextHistory-106] + _ = x[actNextSelected-107] + _ = x[actExecute-108] + _ = x[actExecuteSilent-109] + _ = x[actExecuteMulti-110] + _ = x[actSigStop-111] + _ = x[actFirst-112] + _ = x[actLast-113] + _ = x[actReload-114] + _ = x[actReloadSync-115] + _ = x[actDisableSearch-116] + _ = x[actEnableSearch-117] + _ = x[actSelect-118] + _ = x[actDeselect-119] + _ = x[actUnbind-120] + _ = x[actRebind-121] + _ = x[actBecome-122] + _ = x[actShowHeader-123] + _ = x[actHideHeader-124] + _ = x[actBell-125] } -const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeaderactBell" +const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactChangeNthactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformNthactTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeaderactBell" -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, 1220, 1238, 1255, 1273, 1282, 1292, 1308, 1330, 1343, 1359, 1371, 1385, 1401, 1419, 1439, 1461, 1475, 1490, 1498, 1504, 1518, 1533, 1543, 1559, 1574, 1584, 1592, 1599, 1608, 1621, 1637, 1652, 1661, 1672, 1681, 1690, 1699, 1712, 1725, 1732} +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, 1714, 1727, 1740, 1747} func (i actionType) String() string { if i < 0 || i >= actionType(len(_actionType_index)-1) { diff --git a/src/options.go b/src/options.go index 2b9a5196..5865f0ea 100644 --- a/src/options.go +++ b/src/options.go @@ -1332,7 +1332,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)|transform|change-(?:preview-window|preview|multi|nth)|(?:re|un)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)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print|search)`) splitRegexp = regexp.MustCompile("[,:]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") } @@ -1740,6 +1740,8 @@ func isExecuteAction(str string) actionType { return actTransformHeaderLabel case "transform-header": return actTransformHeader + case "transform-nth": + return actTransformNth case "transform-prompt": return actTransformPrompt case "transform-query": diff --git a/src/terminal.go b/src/terminal.go index 4d58ca8c..56b70e9b 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -531,6 +531,7 @@ const ( actTransformInputLabel actTransformHeader actTransformHeaderLabel + actTransformNth actTransformPreviewLabel actTransformPrompt actTransformQuery @@ -1065,6 +1066,7 @@ func (t *Terminal) environImpl(forPreview bool) []string { env = append(env, fmt.Sprintf("FZF_POS=%d", util.Min(t.merger.Length(), t.cy+1))) env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_LINE=%d", t.clickHeaderLine)) env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_COLUMN=%d", t.clickHeaderColumn)) + env = t.addClickHeaderWord(env) // Add preview environment variables if preview is enabled pwindowSize := t.pwindowSize() @@ -1393,6 +1395,8 @@ func (t *Terminal) changeHeader(header string) bool { } needFullRedraw := len(t.header0) != len(lines) t.header0 = lines + t.clickHeaderLine = 0 + t.clickHeaderColumn = 0 return needFullRedraw } @@ -4089,6 +4093,64 @@ func (t *Terminal) currentIndex() int32 { return minItem.Index() } +func (t *Terminal) addClickHeaderWord(env []string) []string { + /* + * echo $'HL1\nHL2' | fzf --header-lines 3 --header $'H1\nH2' --header-lines-border --bind 'click-header:preview:env | grep FZF_CLICK' + * + * REVERSE DEFAULT + * H1 1 1 + * H2 2 HL2 2 + * ------- HL1 3 + * HL1 3 ------- + * HL2 4 H1 4 + * 5 H2 5 + */ + lineNum := t.clickHeaderLine - 1 + if lineNum < 0 { + // Never clicked on the header + return env + } + + var line string + if t.layout == layoutReverse { + if lineNum < len(t.header0) { + line = t.header0[lineNum] + } else if lineNum-len(t.header0) < len(t.header) { + line = t.header[lineNum-len(t.header0)] + } + } else { + // NOTE: t.header is padded with empty strings so that its size is equal to t.headerLines + if lineNum < len(t.header) { + line = t.header[len(t.header)-lineNum-1] + } else if lineNum-len(t.header) < len(t.header0) { + line = t.header0[lineNum-len(t.header)] + } + } + if len(line) == 0 { + return env + } + + colNum := t.clickHeaderColumn - 1 + words := Tokenize(line, t.delimiter) + for idx, token := range words { + prefixWidth := int(token.prefixLength) + word := token.text.ToString() + trimmed := strings.TrimSpace(word) + trimWidth, _ := util.RunesWidth([]rune(trimmed), prefixWidth, t.tabstop, math.MaxInt32) + + if colNum >= prefixWidth && colNum < prefixWidth+trimWidth { + env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_WORD=%s", trimmed)) + nth := fmt.Sprintf("FZF_CLICK_HEADER_NTH=%d", idx+1) + if idx == len(words)-1 { + nth += ".." + } + env = append(env, nth) + return env + } + } + return env +} + // Loop is called to start Terminal I/O func (t *Terminal) Loop() error { // prof := profile.Start(profile.ProfilePath("/tmp/")) @@ -4833,11 +4895,14 @@ func (t *Terminal) Loop() error { } t.multi = multi req(reqList, reqInfo) - case actChangeNth: - changed = true + case actChangeNth, actTransformNth: + expr := a.a + if a.t == actTransformNth { + expr = t.captureLine(a.a) + } // Split nth expression - tokens := strings.Split(a.a, "|") + tokens := strings.Split(expr, "|") if nth, err := splitNth(tokens[0]); err == nil { // Changed newNth = &nth @@ -4845,12 +4910,15 @@ func (t *Terminal) Loop() error { // The default newNth = &t.nth } - t.nthCurrent = *newNth // Cycle if len(tokens) > 1 { a.a = strings.Join(append(tokens[1:], tokens[0]), "|") } - t.forceRerenderList() + if !compareRanges(t.nthCurrent, *newNth) { + changed = true + t.nthCurrent = *newNth + t.forceRerenderList() + } case actChangeQuery: t.input = []rune(a.a) t.cx = len(t.input) diff --git a/src/tokenizer.go b/src/tokenizer.go index e5a8e977..f5d1483b 100644 --- a/src/tokenizer.go +++ b/src/tokenizer.go @@ -22,6 +22,18 @@ func (r Range) IsFull() bool { return r.begin == rangeEllipsis && r.end == rangeEllipsis } +func compareRanges(r1 []Range, r2 []Range) bool { + if len(r1) != len(r2) { + return false + } + for idx := range r1 { + if r1[idx] != r2[idx] { + return false + } + } + return true +} + func RangesToString(ranges []Range) string { strs := []string{} for _, r := range ranges { -- cgit v1.2.3