From b2ecb6352c2ce516a15edce3ea711623ed8a1375 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 14 Jun 2024 21:33:42 +0900 Subject: [PATCH] Make GET endpoint available from 'execute' and 'transform' actions --- CHANGELOG.md | 9 +++++++++ src/actiontype_string.go | 9 ++++----- src/server.go | 26 ++++++++++---------------- src/terminal.go | 33 +++++++++++++++++++++++++++------ 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae257ab..4b6cf01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ CHANGELOG 0.53.1 ------ - 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 ------ diff --git a/src/actiontype_string.go b/src/actiontype_string.go index 2a597ea..91c2a7f 100644 --- a/src/actiontype_string.go +++ b/src/actiontype_string.go @@ -117,14 +117,13 @@ func _() { _ = x[actUnbind-106] _ = x[actRebind-107] _ = x[actBecome-108] - _ = x[actResponse-109] - _ = x[actShowHeader-110] - _ = x[actHideHeader-111] + _ = x[actShowHeader-109] + _ = x[actHideHeader-110] } -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 { if i < 0 || i >= actionType(len(_actionType_index)-1) { diff --git a/src/server.go b/src/server.go index 0f325d8..5757e16 100644 --- a/src/server.go +++ b/src/server.go @@ -38,9 +38,9 @@ const ( ) type httpServer struct { - apiKey []byte - actionChannel chan []*action - responseChannel chan string + apiKey []byte + actionChannel chan []*action + getHandler func(getParams) string } type listenAddress struct { @@ -73,7 +73,7 @@ func parseListenAddress(address string) (listenAddress, error) { 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 port := address.port apiKey := os.Getenv("FZF_API_KEY") @@ -99,9 +99,9 @@ func startHttpServer(address listenAddress, actionChannel chan []*action, respon } server := httpServer{ - apiKey: []byte(apiKey), - actionChannel: actionChannel, - responseChannel: responseChannel, + apiKey: []byte(apiKey), + actionChannel: actionChannel, + getHandler: getHandler, } go func() { @@ -165,17 +165,11 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string { case 0: getMatch := getRegex.FindStringSubmatch(text) if len(getMatch) > 0 { - server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}} - select { - case response := <-server.responseChannel: + response := server.getHandler(parseGetParams(getMatch[1])) + if len(response) > 0 { 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") { return bad("invalid request method") } diff --git a/src/terminal.go b/src/terminal.go index 7307c17..193021f 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -318,7 +318,6 @@ type Terminal struct { startChan chan fitpad killChan chan bool serverInputChan chan []*action - serverOutputChan chan string eventChan chan tui.Event slab *util.Slab theme *tui.ColorTheme @@ -497,7 +496,6 @@ const ( actUnbind actRebind actBecome - actResponse actShowHeader actHideHeader ) @@ -839,7 +837,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor startChan: make(chan fitpad, 1), killChan: make(chan bool), serverInputChan: make(chan []*action, 100), - serverOutputChan: make(chan string), eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar) tui: renderer, ttyin: ttyin, @@ -893,7 +890,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor _, t.hasLoadActions = t.keymap[tui.Load.AsEvent()] 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 { return nil, err } @@ -2996,11 +2993,14 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo } t.tui.Pause(true) + t.mutex.Unlock() cmd.Run() + t.mutex.Lock() t.tui.Resume(true, false) t.redraw() t.refresh() } else { + t.mutex.Unlock() if capture { out, _ := cmd.StdoutPipe() reader := bufio.NewReader(out) @@ -3016,6 +3016,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo } else { cmd.Run() } + t.mutex.Lock() } t.executing.Set(false) removeFiles(tempFiles) @@ -3719,8 +3720,6 @@ func (t *Terminal) Loop() error { doAction = func(a *action) bool { switch a.t { case actIgnore, actStart, actClick: - case actResponse: - t.serverOutputChan <- t.dumpStatus(parseGetParams(a.a)) case actBecome: valid, list := t.buildPlusList(a.a, false) 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 { + if !t.tryLock(channelTimeout) { + return "" + } + defer t.mutex.Unlock() + selectedItems := t.sortSelected() selected := make([]StatusItem, util.Max(0, util.Min(params.limit, len(selectedItems)-params.offset))) for i := range selected {