Implement asynchronous transform actions

Close #4418

Example:

    fzf --bind 'focus:&transform-header(sleep 2; date; echo {})'
This commit is contained in:
Junegunn Choi
2025-06-11 01:38:23 +09:00
parent 3b68dcdd81
commit 1d9bba53ad
3 changed files with 340 additions and 196 deletions
+55 -39
View File
@@ -113,48 +113,64 @@ func _() {
_ = x[actTransformPrompt-102] _ = x[actTransformPrompt-102]
_ = x[actTransformQuery-103] _ = x[actTransformQuery-103]
_ = x[actTransformSearch-104] _ = x[actTransformSearch-104]
_ = x[actSearch-105] _ = x[actAsyncTransform-105]
_ = x[actPreview-106] _ = x[actAsyncTransformBorderLabel-106]
_ = x[actPreviewTop-107] _ = x[actAsyncTransformGhost-107]
_ = x[actPreviewBottom-108] _ = x[actAsyncTransformHeader-108]
_ = x[actPreviewUp-109] _ = x[actAsyncTransformFooter-109]
_ = x[actPreviewDown-110] _ = x[actAsyncTransformHeaderLabel-110]
_ = x[actPreviewPageUp-111] _ = x[actAsyncTransformFooterLabel-111]
_ = x[actPreviewPageDown-112] _ = x[actAsyncTransformInputLabel-112]
_ = x[actPreviewHalfPageUp-113] _ = x[actAsyncTransformListLabel-113]
_ = x[actPreviewHalfPageDown-114] _ = x[actAsyncTransformNth-114]
_ = x[actPrevHistory-115] _ = x[actAsyncTransformPointer-115]
_ = x[actPrevSelected-116] _ = x[actAsyncTransformPreviewLabel-116]
_ = x[actPrint-117] _ = x[actAsyncTransformPrompt-117]
_ = x[actPut-118] _ = x[actAsyncTransformQuery-118]
_ = x[actNextHistory-119] _ = x[actAsyncTransformSearch-119]
_ = x[actNextSelected-120] _ = x[actSearch-120]
_ = x[actExecute-121] _ = x[actPreview-121]
_ = x[actExecuteSilent-122] _ = x[actPreviewTop-122]
_ = x[actExecuteMulti-123] _ = x[actPreviewBottom-123]
_ = x[actSigStop-124] _ = x[actPreviewUp-124]
_ = x[actFirst-125] _ = x[actPreviewDown-125]
_ = x[actLast-126] _ = x[actPreviewPageUp-126]
_ = x[actReload-127] _ = x[actPreviewPageDown-127]
_ = x[actReloadSync-128] _ = x[actPreviewHalfPageUp-128]
_ = x[actDisableSearch-129] _ = x[actPreviewHalfPageDown-129]
_ = x[actEnableSearch-130] _ = x[actPrevHistory-130]
_ = x[actSelect-131] _ = x[actPrevSelected-131]
_ = x[actDeselect-132] _ = x[actPrint-132]
_ = x[actUnbind-133] _ = x[actPut-133]
_ = x[actRebind-134] _ = x[actNextHistory-134]
_ = x[actToggleBind-135] _ = x[actNextSelected-135]
_ = x[actBecome-136] _ = x[actExecute-136]
_ = x[actShowHeader-137] _ = x[actExecuteSilent-137]
_ = x[actHideHeader-138] _ = x[actExecuteMulti-138]
_ = x[actBell-139] _ = x[actSigStop-139]
_ = x[actExclude-140] _ = x[actFirst-140]
_ = x[actExcludeMulti-141] _ = x[actLast-141]
_ = x[actReload-142]
_ = x[actReloadSync-143]
_ = x[actDisableSearch-144]
_ = x[actEnableSearch-145]
_ = x[actSelect-146]
_ = x[actDeselect-147]
_ = x[actUnbind-148]
_ = x[actRebind-149]
_ = x[actToggleBind-150]
_ = x[actBecome-151]
_ = x[actShowHeader-152]
_ = x[actHideHeader-153]
_ = x[actBell-154]
_ = x[actExclude-155]
_ = x[actExcludeMulti-156]
_ = x[actAsync-157]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti" const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactAsyncTransformactAsyncTransformBorderLabelactAsyncTransformGhostactAsyncTransformHeaderactAsyncTransformFooteractAsyncTransformHeaderLabelactAsyncTransformFooterLabelactAsyncTransformInputLabelactAsyncTransformListLabelactAsyncTransformNthactAsyncTransformPointeractAsyncTransformPreviewLabelactAsyncTransformPromptactAsyncTransformQueryactAsyncTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1557, 1567, 1580, 1596, 1608, 1622, 1638, 1656, 1676, 1698, 1712, 1727, 1735, 1741, 1755, 1770, 1780, 1796, 1811, 1821, 1829, 1836, 1845, 1858, 1874, 1889, 1898, 1909, 1918, 1927, 1940, 1949, 1962, 1975, 1982, 1992, 2007} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1565, 1593, 1615, 1638, 1661, 1689, 1717, 1744, 1770, 1790, 1814, 1843, 1866, 1888, 1911, 1920, 1930, 1943, 1959, 1971, 1985, 2001, 2019, 2039, 2061, 2075, 2090, 2098, 2104, 2118, 2133, 2143, 2159, 2174, 2184, 2192, 2199, 2208, 2221, 2237, 2252, 2261, 2272, 2281, 2290, 2303, 2312, 2325, 2338, 2345, 2355, 2370, 2378}
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) {
+32 -2
View File
@@ -1435,9 +1435,9 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search)`) `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|&?transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|&?transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^&?[a-z-]+")
} }
func maskActionContents(action string) string { func maskActionContents(action string) string {
@@ -1892,6 +1892,36 @@ func isExecuteAction(str string) actionType {
return actTransformQuery return actTransformQuery
case "transform-search": case "transform-search":
return actTransformSearch return actTransformSearch
case "&transform":
return actAsyncTransform
case "&transform-list-label":
return actAsyncTransformListLabel
case "&transform-border-label":
return actAsyncTransformBorderLabel
case "&transform-preview-label":
return actAsyncTransformPreviewLabel
case "&transform-input-label":
return actAsyncTransformInputLabel
case "&transform-header-label":
return actAsyncTransformHeaderLabel
case "&transform-footer-label":
return actAsyncTransformFooterLabel
case "&transform-footer":
return actAsyncTransformFooter
case "&transform-header":
return actAsyncTransformHeader
case "&transform-ghost":
return actAsyncTransformGhost
case "&transform-nth":
return actAsyncTransformNth
case "&transform-pointer":
return actAsyncTransformPointer
case "&transform-prompt":
return actAsyncTransformPrompt
case "&transform-query":
return actAsyncTransformQuery
case "&transform-search":
return actAsyncTransformSearch
case "search": case "search":
return actSearch return actSearch
} }
+175 -77
View File
@@ -388,6 +388,7 @@ type Terminal struct {
startChan chan fitpad startChan chan fitpad
killChan chan bool killChan chan bool
serverInputChan chan []*action serverInputChan chan []*action
callbackChan chan func()
keyChan chan tui.Event keyChan chan tui.Event
eventChan chan tui.Event eventChan chan tui.Event
slab *util.Slab slab *util.Slab
@@ -489,6 +490,7 @@ const (
actBackwardDeleteCharEof actBackwardDeleteCharEof
actBackwardWord actBackwardWord
actCancel actCancel
actChangeBorderLabel actChangeBorderLabel
actChangeGhost actChangeGhost
actChangeHeader actChangeHeader
@@ -505,6 +507,7 @@ const (
actChangePreviewWindow actChangePreviewWindow
actChangePrompt actChangePrompt
actChangeQuery actChangeQuery
actClearScreen actClearScreen
actClearQuery actClearQuery
actClearSelection actClearSelection
@@ -561,6 +564,7 @@ const (
actHidePreview actHidePreview
actTogglePreview actTogglePreview
actTogglePreviewWrap actTogglePreviewWrap
actTransform actTransform
actTransformBorderLabel actTransformBorderLabel
actTransformGhost actTransformGhost
@@ -576,6 +580,23 @@ const (
actTransformPrompt actTransformPrompt
actTransformQuery actTransformQuery
actTransformSearch actTransformSearch
actAsyncTransform
actAsyncTransformBorderLabel
actAsyncTransformGhost
actAsyncTransformHeader
actAsyncTransformFooter
actAsyncTransformHeaderLabel
actAsyncTransformFooterLabel
actAsyncTransformInputLabel
actAsyncTransformListLabel
actAsyncTransformNth
actAsyncTransformPointer
actAsyncTransformPreviewLabel
actAsyncTransformPrompt
actAsyncTransformQuery
actAsyncTransformSearch
actSearch actSearch
actPreview actPreview
actPreviewTop actPreviewTop
@@ -613,6 +634,7 @@ const (
actBell actBell
actExclude actExclude
actExcludeMulti actExcludeMulti
actAsync
) )
func (a actionType) Name() string { func (a actionType) Name() string {
@@ -623,10 +645,34 @@ func processExecution(action actionType) bool {
switch action { switch action {
case actTransform, case actTransform,
actTransformBorderLabel, actTransformBorderLabel,
actTransformGhost,
actTransformHeader, actTransformHeader,
actTransformFooter,
actTransformHeaderLabel,
actTransformFooterLabel,
actTransformInputLabel,
actTransformListLabel,
actTransformNth,
actTransformPointer,
actTransformPreviewLabel, actTransformPreviewLabel,
actTransformPrompt, actTransformPrompt,
actTransformQuery, actTransformQuery,
actTransformSearch,
actAsyncTransform,
actAsyncTransformBorderLabel,
actAsyncTransformGhost,
actAsyncTransformHeader,
actAsyncTransformFooter,
actAsyncTransformHeaderLabel,
actAsyncTransformFooterLabel,
actAsyncTransformInputLabel,
actAsyncTransformListLabel,
actAsyncTransformNth,
actAsyncTransformPointer,
actAsyncTransformPreviewLabel,
actAsyncTransformPrompt,
actAsyncTransformQuery,
actAsyncTransformSearch,
actPreview, actPreview,
actChangePreview, actChangePreview,
actRefreshPreview, actRefreshPreview,
@@ -773,7 +819,7 @@ func mayTriggerPreview(opts *Options) bool {
for _, actions := range opts.Keymap { for _, actions := range opts.Keymap {
for _, action := range actions { for _, action := range actions {
switch action.t { switch action.t {
case actPreview, actChangePreview, actTransform: case actPreview, actChangePreview, actTransform, actAsyncTransform:
return true return true
} }
} }
@@ -987,6 +1033,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
startChan: make(chan fitpad, 1), startChan: make(chan fitpad, 1),
killChan: make(chan bool), killChan: make(chan bool),
serverInputChan: make(chan []*action, 100), serverInputChan: make(chan []*action, 100),
callbackChan: make(chan func(), 100),
keyChan: make(chan tui.Event), keyChan: make(chan tui.Event),
eventChan: make(chan tui.Event, 6), // start | (load + result + zero|one) | (focus) | (resize) eventChan: make(chan tui.Event, 6), // start | (load + result + zero|one) | (focus) | (resize)
tui: renderer, tui: renderer,
@@ -4291,6 +4338,32 @@ func (t *Terminal) captureLines(template string) string {
return t.executeCommand(template, false, true, true, false, "") return t.executeCommand(template, false, true, true, false, "")
} }
func (t *Terminal) captureAsync(template string, firstLineOnly bool, callback func(string)) {
_, list := t.buildPlusList(template, false)
command, tempFiles := t.replacePlaceholder(template, false, string(t.input), list)
go func() {
cmd := t.executor.ExecCommand(command, false)
cmd.Env = t.environ()
out, _ := cmd.StdoutPipe()
reader := bufio.NewReader(out)
var output string
if err := cmd.Start(); err == nil {
if firstLineOnly {
output, _ = reader.ReadString('\n')
output = strings.TrimRight(output, "\r\n")
} else {
bytes, _ := io.ReadAll(reader)
output = string(bytes)
}
cmd.Wait()
}
removeFiles(tempFiles)
t.callbackChan <- func() { callback(output) }
}()
}
func (t *Terminal) executeCommand(template string, forcePlus bool, background bool, capture bool, firstLineOnly bool, info string) string { func (t *Terminal) executeCommand(template string, forcePlus bool, background bool, capture bool, firstLineOnly bool, info string) string {
line := "" line := ""
valid, list := t.buildPlusList(template, forcePlus) valid, list := t.buildPlusList(template, forcePlus)
@@ -5089,11 +5162,27 @@ func (t *Terminal) Loop() error {
barrier <- true barrier <- true
needBarrier = false needBarrier = false
} }
// These variables are defined outside the loop to be accessible from closures
events := []util.EventType{}
changed := false
var newNth *[]Range
req := func(evts ...util.EventType) {
for _, event := range evts {
events = append(events, event)
if event == reqClose || event == reqQuit {
looping = false
}
}
}
// The main event loop
for loopIndex := int64(0); looping; loopIndex++ { for loopIndex := int64(0); looping; loopIndex++ {
var newCommand *commandSpec var newCommand *commandSpec
var newNth *[]Range
var reloadSync bool var reloadSync bool
changed := false events = []util.EventType{}
changed = false
newNth = nil
beof := false beof := false
queryChanged := false queryChanged := false
denylist := []int32{} denylist := []int32{}
@@ -5110,6 +5199,7 @@ func (t *Terminal) Loop() error {
var event tui.Event var event tui.Event
actions := []*action{} actions := []*action{}
callbacks := []func(){}
select { select {
case event = <-t.keyChan: case event = <-t.keyChan:
needBarrier = true needBarrier = true
@@ -5141,6 +5231,20 @@ func (t *Terminal) Loop() error {
} }
} }
} }
case callback := <-t.callbackChan:
event = tui.Invalid.AsEvent()
actions = append(actions, &action{t: actAsync})
callbacks = append(callbacks, callback)
DrainCallback:
for {
select {
case callback = <-t.callbackChan:
callbacks = append(callbacks, callback)
continue DrainCallback
default:
break DrainCallback
}
}
} }
t.mutex.Lock() t.mutex.Lock()
@@ -5155,15 +5259,6 @@ func (t *Terminal) Loop() error {
previousInput := t.input previousInput := t.input
previousCx := t.cx previousCx := t.cx
t.lastKey = event.KeyName() t.lastKey = event.KeyName()
events := []util.EventType{}
req := func(evts ...util.EventType) {
for _, event := range evts {
events = append(events, event)
if event == reqClose || event == reqQuit {
looping = false
}
}
}
updatePreviewWindow := func(forcePreview bool) { updatePreviewWindow := func(forcePreview bool) {
t.resizeWindows(forcePreview, false) t.resizeWindows(forcePreview, false)
req(reqPrompt, reqList, reqInfo, reqHeader, reqFooter) req(reqPrompt, reqList, reqInfo, reqHeader, reqFooter)
@@ -5238,9 +5333,29 @@ func (t *Terminal) Loop() error {
// actions to allow changing the query even when the input is hidden // actions to allow changing the query even when the input is hidden
// e.g. fzf --no-input --bind 'space:show-input+change-query(foo)+hide-input' // e.g. fzf --no-input --bind 'space:show-input+change-query(foo)+hide-input'
currentInput := t.input currentInput := t.input
capture := func(firstLineOnly bool, callback func(string)) {
if a.t >= actAsyncTransform {
// &transform-*
t.captureAsync(a.a, firstLineOnly, callback)
} else if a.t >= actTransform {
// transform-*
if firstLineOnly {
callback(t.captureLine(a.a))
} else {
callback(t.captureLines(a.a))
}
} else {
// change-*
callback(a.a)
}
}
Action: Action:
switch a.t { switch a.t {
case actIgnore, actStart, actClick: case actIgnore, actStart, actClick:
case actAsync:
for _, callback := range callbacks {
callback()
}
case actBecome: case actBecome:
valid, list := t.buildPlusList(a.a, false) valid, list := t.buildPlusList(a.a, false)
if valid { if valid {
@@ -5333,15 +5448,17 @@ func (t *Terminal) Loop() error {
t.previewed.version = 0 t.previewed.version = 0
req(reqPreviewRefresh) req(reqPreviewRefresh)
} }
case actTransformPrompt: case actTransformPrompt, actAsyncTransformPrompt:
prompt := t.captureLine(a.a) capture(true, func(prompt string) {
t.promptString = prompt t.promptString = prompt
t.prompt, t.promptLen = t.parsePrompt(prompt) t.prompt, t.promptLen = t.parsePrompt(prompt)
req(reqPrompt) req(reqPrompt)
case actTransformQuery: })
query := t.captureLine(a.a) case actTransformQuery, actAsyncTransformQuery:
capture(true, func(query string) {
t.input = []rune(query) t.input = []rune(query)
t.cx = len(t.input) t.cx = len(t.input)
})
case actToggleSort: case actToggleSort:
t.sort = !t.sort t.sort = !t.sort
changed = true changed = true
@@ -5399,12 +5516,8 @@ func (t *Terminal) Loop() error {
} }
t.multi = multi t.multi = multi
req(reqList, reqInfo) req(reqList, reqInfo)
case actChangeNth, actTransformNth: case actChangeNth, actTransformNth, actAsyncTransformNth:
expr := a.a capture(true, func(expr string) {
if a.t == actTransformNth {
expr = t.captureLine(a.a)
}
// Split nth expression // Split nth expression
tokens := strings.Split(expr, "|") tokens := strings.Split(expr, "|")
if nth, err := splitNth(tokens[0]); err == nil { if nth, err := splitNth(tokens[0]); err == nil {
@@ -5423,14 +5536,12 @@ func (t *Terminal) Loop() error {
t.nthCurrent = *newNth t.nthCurrent = *newNth
t.forceRerenderList() t.forceRerenderList()
} }
})
case actChangeQuery: case actChangeQuery:
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)
case actChangeHeader, actTransformHeader: case actChangeHeader, actTransformHeader, actAsyncTransformHeader:
header := a.a capture(false, func(header string) {
if a.t == actTransformHeader {
header = t.captureLines(a.a)
}
if t.changeHeader(header) { if t.changeHeader(header) {
if t.headerWindow != nil { if t.headerWindow != nil {
// Need to resize header window // Need to resize header window
@@ -5441,77 +5552,66 @@ func (t *Terminal) Loop() error {
} else { } else {
req(reqHeader) req(reqHeader)
} }
case actChangeFooter, actTransformFooter: })
footer := a.a case actChangeFooter, actTransformFooter, actAsyncTransformFooter:
if a.t == actTransformFooter { capture(false, func(footer string) {
footer = t.captureLines(a.a)
}
if t.changeFooter(footer) { if t.changeFooter(footer) {
req(reqFullRedraw) req(reqFullRedraw)
} else { } else {
req(reqFooter) req(reqFooter)
} }
case actChangeHeaderLabel, actTransformHeaderLabel: })
label := a.a case actChangeHeaderLabel, actTransformHeaderLabel, actAsyncTransformHeaderLabel:
if a.t == actTransformHeaderLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.headerLabelOpts.label = label t.headerLabelOpts.label = label
t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false) t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false)
req(reqRedrawHeaderLabel) req(reqRedrawHeaderLabel)
case actChangeFooterLabel, actTransformFooterLabel: })
label := a.a case actChangeFooterLabel, actTransformFooterLabel, actAsyncTransformFooterLabel:
if a.t == actTransformFooterLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.footerLabelOpts.label = label t.footerLabelOpts.label = label
t.footerLabel, t.footerLabelLen = t.ansiLabelPrinter(label, &tui.ColFooterLabel, false) t.footerLabel, t.footerLabelLen = t.ansiLabelPrinter(label, &tui.ColFooterLabel, false)
req(reqRedrawFooterLabel) req(reqRedrawFooterLabel)
case actChangeInputLabel, actTransformInputLabel: })
label := a.a case actChangeInputLabel, actTransformInputLabel, actAsyncTransformInputLabel:
if a.t == actTransformInputLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.inputLabelOpts.label = label t.inputLabelOpts.label = label
if t.inputBorder != nil { if t.inputBorder != nil {
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(label, &tui.ColInputLabel, false) t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(label, &tui.ColInputLabel, false)
req(reqRedrawInputLabel) req(reqRedrawInputLabel)
} }
case actChangeListLabel, actTransformListLabel: })
label := a.a case actChangeListLabel, actTransformListLabel, actAsyncTransformListLabel:
if a.t == actTransformListLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.listLabelOpts.label = label t.listLabelOpts.label = label
if t.wborder != nil { if t.wborder != nil {
t.listLabel, t.listLabelLen = t.ansiLabelPrinter(label, &tui.ColListLabel, false) t.listLabel, t.listLabelLen = t.ansiLabelPrinter(label, &tui.ColListLabel, false)
req(reqRedrawListLabel) req(reqRedrawListLabel)
} }
case actChangeBorderLabel, actTransformBorderLabel: })
label := a.a case actChangeBorderLabel, actTransformBorderLabel, actAsyncTransformBorderLabel:
if a.t == actTransformBorderLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.borderLabelOpts.label = label t.borderLabelOpts.label = label
if t.border != nil { if t.border != nil {
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false) t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel) req(reqRedrawBorderLabel)
} }
case actChangePreviewLabel, actTransformPreviewLabel: })
label := a.a case actChangePreviewLabel, actTransformPreviewLabel, actAsyncTransformPreviewLabel:
if a.t == actTransformPreviewLabel { capture(true, func(label string) {
label = t.captureLine(a.a)
}
t.previewLabelOpts.label = label t.previewLabelOpts.label = label
if t.pborder != nil { if t.pborder != nil {
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false) t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel) req(reqRedrawPreviewLabel)
} }
case actTransform: })
body := t.captureLines(a.a) case actTransform, actAsyncTransform:
capture(false, func(body string) {
if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil { if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil {
return doActions(actions) // NOTE: We're not properly passing the return value here
doActions(actions)
} }
})
case actChangePrompt: case actChangePrompt:
t.promptString = a.a t.promptString = a.a
t.prompt, t.promptLen = t.parsePrompt(a.a) t.prompt, t.promptLen = t.parsePrompt(a.a)
@@ -5933,10 +6033,12 @@ func (t *Terminal) Loop() error {
override := []rune(a.a) override := []rune(a.a)
t.inputOverride = &override t.inputOverride = &override
changed = true changed = true
case actTransformSearch: case actTransformSearch, actAsyncTransformSearch:
override := []rune(t.captureLine(a.a)) capture(true, func(query string) {
override := []rune(query)
t.inputOverride = &override t.inputOverride = &override
changed = true changed = true
})
case actEnableSearch: case actEnableSearch:
t.paused = false t.paused = false
changed = true changed = true
@@ -6276,20 +6378,15 @@ func (t *Terminal) Loop() error {
} }
} }
} }
case actChangeGhost, actTransformGhost: case actChangeGhost, actTransformGhost, actAsyncTransformGhost:
ghost := a.a capture(true, func(ghost string) {
if a.t == actTransformGhost {
ghost = t.captureLine(a.a)
}
t.ghost = ghost t.ghost = ghost
if len(t.input) == 0 { if len(t.input) == 0 {
req(reqPrompt) req(reqPrompt)
} }
case actChangePointer, actTransformPointer: })
pointer := a.a case actChangePointer, actTransformPointer, actAsyncTransformPointer:
if a.t == actTransformPointer { capture(true, func(pointer string) {
pointer = t.captureLine(a.a)
}
length := uniseg.StringWidth(pointer) length := uniseg.StringWidth(pointer)
if length <= 2 { if length <= 2 {
if length != t.pointerLen { if length != t.pointerLen {
@@ -6300,6 +6397,7 @@ func (t *Terminal) Loop() error {
t.pointerEmpty = strings.Repeat(" ", t.pointerLen) t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
req(reqList) req(reqList)
} }
})
case actChangePreview: case actChangePreview:
if t.previewOpts.command != a.a { if t.previewOpts.command != a.a {
t.previewOpts.command = a.a t.previewOpts.command = a.a