Add --listen=HTTP_PORT option to receive actions

Supersedes #2019

See also:
* #1728
* https://github.com/junegunn/fzf.vim/pull/1044
This commit is contained in:
Junegunn Choi
2022-12-18 00:22:15 +09:00
parent 51c518da1e
commit 1ba7484d60
5 changed files with 337 additions and 249 deletions

View File

@@ -3,8 +3,10 @@ package fzf
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"os"
"os/signal"
"regexp"
@@ -167,6 +169,7 @@ type Terminal struct {
padding [4]sizeSpec
strong tui.Attr
unicode bool
listenPort int
borderShape tui.BorderShape
cleanExit bool
paused bool
@@ -200,6 +203,7 @@ type Terminal struct {
sigstop bool
startChan chan fitpad
killChan chan int
serverChan chan []*action
slab *util.Slab
theme *tui.ColorTheme
tui tui.Renderer
@@ -481,7 +485,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
}
var previewBox *util.EventBox
showPreviewWindow := len(opts.Preview.command) > 0 && !opts.Preview.hidden
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) {
// We need to start previewer if HTTP server is enabled even when --preview option is not specified
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) || opts.ListenPort > 0 {
previewBox = util.NewEventBox()
}
strongAttr := tui.Bold
@@ -556,6 +561,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
margin: opts.Margin,
padding: opts.Padding,
unicode: opts.Unicode,
listenPort: opts.ListenPort,
borderShape: opts.BorderShape,
borderLabel: nil,
borderLabelOpts: opts.BorderLabel,
@@ -595,6 +601,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
theme: opts.Theme,
startChan: make(chan fitpad, 1),
killChan: make(chan int),
serverChan: make(chan []*action),
tui: renderer,
initFunc: func() { renderer.Init() },
executing: util.NewAtomicBool(false)}
@@ -619,6 +626,39 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
return &t
}
func (t *Terminal) startServer() {
if t.listenPort == 0 {
return
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusBadRequest)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
response := ""
actions := parseSingleActionList(string(body), func(message string) {
response = message
})
if len(response) > 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, response)
return
}
t.serverChan <- actions
})
go func() {
http.ListenAndServe(fmt.Sprintf(":%d", t.listenPort), nil)
}()
}
func borderLines(shape tui.BorderShape) int {
switch shape {
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
@@ -2256,6 +2296,9 @@ func (t *Terminal) Loop() {
env = append(env, "FZF_PREVIEW_"+lines)
env = append(env, columns)
env = append(env, "FZF_PREVIEW_"+columns)
if t.listenPort > 0 {
env = append(env, fmt.Sprintf("FZF_LISTEN_PORT=%d", t.listenPort))
}
cmd.Env = env
}
@@ -2492,6 +2535,16 @@ func (t *Terminal) Loop() {
looping := true
_, startEvent := t.keymap[tui.Start.AsEvent()]
t.startServer()
eventChan := make(chan tui.Event)
needBarrier := true
barrier := make(chan bool)
go func() {
for {
<-barrier
eventChan <- t.tui.GetChar()
}
}()
for looping {
var newCommand *string
changed := false
@@ -2499,11 +2552,21 @@ func (t *Terminal) Loop() {
queryChanged := false
var event tui.Event
actions := []*action{}
if startEvent {
event = tui.Start.AsEvent()
startEvent = false
} else {
event = t.tui.GetChar()
if needBarrier {
barrier <- true
}
select {
case event = <-eventChan:
needBarrier = true
case actions = <-t.serverChan:
event = tui.Invalid.AsEvent()
needBarrier = false
}
}
t.mutex.Lock()
@@ -3043,7 +3106,9 @@ func (t *Terminal) Loop() {
}
if t.jumping == jumpDisabled {
actions := t.keymap[event.Comparable()]
if len(actions) == 0 {
actions = t.keymap[event.Comparable()]
}
if len(actions) == 0 && event.Type == tui.Rune {
doAction(&action{t: actRune})
} else if !doActions(actions) {