summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server.go34
-rw-r--r--src/terminal.go79
2 files changed, 97 insertions, 16 deletions
diff --git a/src/server.go b/src/server.go
index c583400f..36980157 100644
--- a/src/server.go
+++ b/src/server.go
@@ -23,11 +23,12 @@ const (
)
type httpServer struct {
- apiKey []byte
- channel chan []*action
+ apiKey []byte
+ actionChannel chan []*action
+ responseChannel chan string
}
-func startHttpServer(port int, channel chan []*action) (error, int) {
+func startHttpServer(port int, actionChannel chan []*action, responseChannel chan string) (error, int) {
if port < 0 {
return nil, port
}
@@ -50,8 +51,9 @@ func startHttpServer(port int, channel chan []*action) (error, int) {
}
server := httpServer{
- apiKey: []byte(os.Getenv("FZF_API_KEY")),
- channel: channel,
+ apiKey: []byte(os.Getenv("FZF_API_KEY")),
+ actionChannel: actionChannel,
+ responseChannel: responseChannel,
}
go func() {
@@ -83,13 +85,18 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
contentLength := 0
apiKey := ""
body := ""
- unauthorized := func(message string) string {
+ answer := func(code string, message string) string {
message += "\n"
- return httpUnauthorized + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message)
+ return code + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message)
+ }
+ unauthorized := func(message string) string {
+ return answer(httpUnauthorized, message)
}
bad := func(message string) string {
- message += "\n"
- return httpBadRequest + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message)
+ return answer(httpBadRequest, message)
+ }
+ good := func(message string) string {
+ return answer(httpOk+"Content-Type: application/json"+crlf, message)
}
conn.SetReadDeadline(time.Now().Add(httpReadTimeout))
scanner := bufio.NewScanner(conn)
@@ -110,7 +117,12 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
text := scanner.Text()
switch section {
case 0:
- if !strings.HasPrefix(text, "POST / HTTP") {
+ // TODO: Parameter support e.g. "GET /?limit=100 HTTP"
+ if strings.HasPrefix(text, "GET / HTTP") {
+ server.actionChannel <- []*action{{t: actResponse}}
+ response := <-server.responseChannel
+ return good(response)
+ } else if !strings.HasPrefix(text, "POST / HTTP") {
return bad("invalid request method")
}
section++
@@ -160,6 +172,6 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
return bad("no action specified")
}
- server.channel <- actions
+ server.actionChannel <- actions
return httpOk
}
diff --git a/src/terminal.go b/src/terminal.go
index 6418a5df..1d8b55ba 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -2,6 +2,7 @@ package fzf
import (
"bufio"
+ "encoding/json"
"fmt"
"io"
"math"
@@ -144,6 +145,23 @@ var emptyLine = itemLine{}
type labelPrinter func(tui.Window, int)
+type StatusItem struct {
+ Index int `json:"index"`
+ Text string `json:"text"`
+}
+
+type Status struct {
+ Reading bool `json:"reading"`
+ Query string `json:"query"`
+ Position int `json:"position"`
+ Sort bool `json:"sort"`
+ TotalCount int `json:"totalCount"`
+ MatchCount int `json:"matchCount"`
+ Current *StatusItem `json:"current"`
+ Matches []StatusItem `json:"matches"`
+ Selected []StatusItem `json:"selected"`
+}
+
// Terminal represents terminal input/output
type Terminal struct {
initDelay time.Duration
@@ -245,7 +263,8 @@ type Terminal struct {
sigstop bool
startChan chan fitpad
killChan chan int
- serverChan chan []*action
+ serverInputChan chan []*action
+ serverOutputChan chan string
eventChan chan tui.Event
slab *util.Slab
theme *tui.ColorTheme
@@ -398,6 +417,7 @@ const (
actUnbind
actRebind
actBecome
+ actResponse
)
type placeholderFlags struct {
@@ -657,7 +677,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
theme: opts.Theme,
startChan: make(chan fitpad, 1),
killChan: make(chan int),
- serverChan: make(chan []*action, 10),
+ serverInputChan: make(chan []*action, 10),
+ serverOutputChan: make(chan string),
eventChan: make(chan tui.Event, 1),
tui: renderer,
initFunc: func() { renderer.Init() },
@@ -703,7 +724,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
if t.listenPort != nil {
- err, port := startHttpServer(*t.listenPort, t.serverChan)
+ err, port := startHttpServer(*t.listenPort, t.serverInputChan, t.serverOutputChan)
if err != nil {
errorExit(err.Error())
}
@@ -2813,7 +2834,7 @@ func (t *Terminal) Loop() {
t.printInfo()
}
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && focusChanged {
- t.serverChan <- onFocus
+ t.serverInputChan <- onFocus
}
if focusChanged || version != t.version {
version = t.version
@@ -2925,7 +2946,7 @@ func (t *Terminal) Loop() {
select {
case event = <-t.eventChan:
needBarrier = !event.Is(tui.Load, tui.One, tui.Zero)
- case actions = <-t.serverChan:
+ case actions = <-t.serverInputChan:
event = tui.Invalid.AsEvent()
needBarrier = false
}
@@ -3000,6 +3021,8 @@ func (t *Terminal) Loop() {
doAction = func(a *action) bool {
switch a.t {
case actIgnore:
+ case actResponse:
+ t.serverOutputChan <- t.dumpStatus()
case actBecome:
valid, list := t.buildPlusList(a.a, false)
if valid {
@@ -3768,3 +3791,49 @@ func (t *Terminal) maxItems() int {
}
return util.Max(max, 0)
}
+
+func (t *Terminal) dumpItem(i *Item) StatusItem {
+ if i == nil {
+ return StatusItem{}
+ }
+ return StatusItem{
+ Index: int(i.Index()),
+ Text: i.AsString(t.ansi),
+ }
+}
+
+const dumpItemLimit = 100 // TODO: Make configurable via GET parameter
+
+func (t *Terminal) dumpStatus() string {
+ selectedItems := t.sortSelected()
+ selected := make([]StatusItem, util.Min(dumpItemLimit, len(selectedItems)))
+ for i, selectedItem := range selectedItems[:len(selected)] {
+ selected[i] = t.dumpItem(selectedItem.item)
+ }
+
+ matches := make([]StatusItem, util.Min(dumpItemLimit, t.merger.Length()))
+ for i := range matches {
+ matches[i] = t.dumpItem(t.merger.Get(i).item)
+ }
+
+ var current *StatusItem
+ currentItem := t.currentItem()
+ if currentItem != nil {
+ item := t.dumpItem(currentItem)
+ current = &item
+ }
+
+ dump := Status{
+ Reading: t.reading,
+ Query: string(t.input),
+ Position: t.cy,
+ Sort: t.sort,
+ TotalCount: t.count,
+ MatchCount: t.merger.Length(),
+ Current: current,
+ Matches: matches,
+ Selected: selected,
+ }
+ bytes, _ := json.Marshal(&dump) // TODO: Errors?
+ return string(bytes)
+}