mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-15 12:13:52 -07:00
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:
@@ -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) {
|
||||
|
Reference in New Issue
Block a user