Make GET endpoint available from 'execute' and 'transform' actions

This commit is contained in:
Junegunn Choi 2024-06-14 21:33:42 +09:00
parent 9dc3ed638a
commit b2ecb6352c
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
4 changed files with 50 additions and 27 deletions

View File

@ -4,6 +4,15 @@ CHANGELOG
0.53.1 0.53.1
------ ------
- Bug fixes and minor improvements - Bug fixes and minor improvements
- Better cache management and improved rendering for `--tail`
- Fixed crash when using `--tiebreak=end` with very long items
- Fixed mouse support on Windows
- zsh 5.0 compatibility (thanks to @LangLangBart)
- Fixed `--walker-skip` to also skip symlinks to directories
- GET endpoint is now available from `execute` and `transform` actions (it used to timeout due to lock conflict)
```sh
fzf --listen --bind 'focus:transform-header:curl -s localhost:$FZF_PORT?limit=0 | jq .'
```
0.53.0 0.53.0
------ ------

View File

@ -117,14 +117,13 @@ func _() {
_ = x[actUnbind-106] _ = x[actUnbind-106]
_ = x[actRebind-107] _ = x[actRebind-107]
_ = x[actBecome-108] _ = x[actBecome-108]
_ = x[actResponse-109] _ = x[actShowHeader-109]
_ = x[actShowHeader-110] _ = x[actHideHeader-110]
_ = x[actHideHeader-111]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactResponseactShowHeaderactHideHeader" const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 407, 421, 435, 446, 457, 475, 492, 499, 518, 530, 544, 553, 568, 580, 593, 604, 615, 627, 641, 662, 677, 692, 709, 716, 721, 730, 741, 752, 765, 780, 791, 804, 811, 824, 837, 854, 869, 882, 896, 910, 926, 946, 958, 981, 999, 1023, 1041, 1058, 1068, 1084, 1106, 1119, 1135, 1147, 1161, 1177, 1195, 1215, 1237, 1251, 1266, 1274, 1280, 1294, 1309, 1319, 1335, 1350, 1360, 1368, 1375, 1384, 1397, 1413, 1428, 1437, 1448, 1457, 1466, 1475, 1486, 1499, 1512} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 407, 421, 435, 446, 457, 475, 492, 499, 518, 530, 544, 553, 568, 580, 593, 604, 615, 627, 641, 662, 677, 692, 709, 716, 721, 730, 741, 752, 765, 780, 791, 804, 811, 824, 837, 854, 869, 882, 896, 910, 926, 946, 958, 981, 999, 1023, 1041, 1058, 1068, 1084, 1106, 1119, 1135, 1147, 1161, 1177, 1195, 1215, 1237, 1251, 1266, 1274, 1280, 1294, 1309, 1319, 1335, 1350, 1360, 1368, 1375, 1384, 1397, 1413, 1428, 1437, 1448, 1457, 1466, 1475, 1488, 1501}
func (i actionType) String() string { func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) { if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@ -38,9 +38,9 @@ const (
) )
type httpServer struct { type httpServer struct {
apiKey []byte apiKey []byte
actionChannel chan []*action actionChannel chan []*action
responseChannel chan string getHandler func(getParams) string
} }
type listenAddress struct { type listenAddress struct {
@ -73,7 +73,7 @@ func parseListenAddress(address string) (listenAddress, error) {
return listenAddress{parts[0], port}, nil return listenAddress{parts[0], port}, nil
} }
func startHttpServer(address listenAddress, actionChannel chan []*action, responseChannel chan string) (net.Listener, int, error) { func startHttpServer(address listenAddress, actionChannel chan []*action, getHandler func(getParams) string) (net.Listener, int, error) {
host := address.host host := address.host
port := address.port port := address.port
apiKey := os.Getenv("FZF_API_KEY") apiKey := os.Getenv("FZF_API_KEY")
@ -99,9 +99,9 @@ func startHttpServer(address listenAddress, actionChannel chan []*action, respon
} }
server := httpServer{ server := httpServer{
apiKey: []byte(apiKey), apiKey: []byte(apiKey),
actionChannel: actionChannel, actionChannel: actionChannel,
responseChannel: responseChannel, getHandler: getHandler,
} }
go func() { go func() {
@ -165,17 +165,11 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
case 0: case 0:
getMatch := getRegex.FindStringSubmatch(text) getMatch := getRegex.FindStringSubmatch(text)
if len(getMatch) > 0 { if len(getMatch) > 0 {
server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}} response := server.getHandler(parseGetParams(getMatch[1]))
select { if len(response) > 0 {
case response := <-server.responseChannel:
return good(response) return good(response)
case <-time.After(channelTimeout):
go func() {
// Drain the channel
<-server.responseChannel
}()
return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`)
} }
return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`)
} else if !strings.HasPrefix(text, "POST / HTTP") { } else if !strings.HasPrefix(text, "POST / HTTP") {
return bad("invalid request method") return bad("invalid request method")
} }

View File

@ -318,7 +318,6 @@ type Terminal struct {
startChan chan fitpad startChan chan fitpad
killChan chan bool killChan chan bool
serverInputChan chan []*action serverInputChan chan []*action
serverOutputChan chan string
eventChan chan tui.Event eventChan chan tui.Event
slab *util.Slab slab *util.Slab
theme *tui.ColorTheme theme *tui.ColorTheme
@ -497,7 +496,6 @@ const (
actUnbind actUnbind
actRebind actRebind
actBecome actBecome
actResponse
actShowHeader actShowHeader
actHideHeader actHideHeader
) )
@ -839,7 +837,6 @@ 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),
serverOutputChan: make(chan string),
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar) eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
tui: renderer, tui: renderer,
ttyin: ttyin, ttyin: ttyin,
@ -893,7 +890,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()] _, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
if t.listenAddr != nil { if t.listenAddr != nil {
listener, port, err := startHttpServer(*t.listenAddr, t.serverInputChan, t.serverOutputChan) listener, port, err := startHttpServer(*t.listenAddr, t.serverInputChan, t.dumpStatus)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2996,11 +2993,14 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
} }
t.tui.Pause(true) t.tui.Pause(true)
t.mutex.Unlock()
cmd.Run() cmd.Run()
t.mutex.Lock()
t.tui.Resume(true, false) t.tui.Resume(true, false)
t.redraw() t.redraw()
t.refresh() t.refresh()
} else { } else {
t.mutex.Unlock()
if capture { if capture {
out, _ := cmd.StdoutPipe() out, _ := cmd.StdoutPipe()
reader := bufio.NewReader(out) reader := bufio.NewReader(out)
@ -3016,6 +3016,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
} else { } else {
cmd.Run() cmd.Run()
} }
t.mutex.Lock()
} }
t.executing.Set(false) t.executing.Set(false)
removeFiles(tempFiles) removeFiles(tempFiles)
@ -3719,8 +3720,6 @@ func (t *Terminal) Loop() error {
doAction = func(a *action) bool { doAction = func(a *action) bool {
switch a.t { switch a.t {
case actIgnore, actStart, actClick: case actIgnore, actStart, actClick:
case actResponse:
t.serverOutputChan <- t.dumpStatus(parseGetParams(a.a))
case actBecome: case actBecome:
valid, list := t.buildPlusList(a.a, false) valid, list := t.buildPlusList(a.a, false)
if valid { if valid {
@ -4708,7 +4707,29 @@ func (t *Terminal) dumpItem(i *Item) StatusItem {
} }
} }
func (t *Terminal) tryLock(timeout time.Duration) bool {
sleepDuration := 10 * time.Millisecond
for {
if t.mutex.TryLock() {
return true
}
timeout -= sleepDuration
if timeout <= 0 {
break
}
time.Sleep(sleepDuration)
}
return false
}
func (t *Terminal) dumpStatus(params getParams) string { func (t *Terminal) dumpStatus(params getParams) string {
if !t.tryLock(channelTimeout) {
return ""
}
defer t.mutex.Unlock()
selectedItems := t.sortSelected() selectedItems := t.sortSelected()
selected := make([]StatusItem, util.Max(0, util.Min(params.limit, len(selectedItems)-params.offset))) selected := make([]StatusItem, util.Max(0, util.Min(params.limit, len(selectedItems)-params.offset)))
for i := range selected { for i := range selected {