mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-10 15:50:56 +00:00
parent
3c34dd8275
commit
fcd7e8768d
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,6 +1,20 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.39.0
|
||||||
|
------
|
||||||
|
- If you use `--listen` option without a port number fzf will automatically
|
||||||
|
allocate an available port and export it as `$FZF_PORT` environment
|
||||||
|
variable.
|
||||||
|
```sh
|
||||||
|
# Automatic port assignment
|
||||||
|
fzf --listen --bind 'start:execute-silent:echo $FZF_PORT > /tmp/fzf-port'
|
||||||
|
|
||||||
|
# Say hello
|
||||||
|
curl "localhost:$(cat /tmp/fzf-port)" -d 'preview:echo Hello, fzf is listening on $FZF_PORT.'
|
||||||
|
```
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
0.38.0
|
0.38.0
|
||||||
------
|
------
|
||||||
- New actions
|
- New actions
|
||||||
|
@ -738,9 +738,12 @@ ncurses finder only after the input stream is complete.
|
|||||||
e.g. \fBfzf --multi | fzf --sync\fR
|
e.g. \fBfzf --multi | fzf --sync\fR
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B "--listen=HTTP_PORT"
|
.B "--listen[=HTTP_PORT]"
|
||||||
Start HTTP server on the given port. It allows external processes to send
|
Start HTTP server on the given port. It allows external processes to send
|
||||||
actions to perform via POST method.
|
actions to perform via POST method. If the port number is omitted or given as
|
||||||
|
0, fzf will choose the port automatically and export it as \fBFZF_PORT\fR
|
||||||
|
environment variable to the child processes started via \fBexecute\fR and
|
||||||
|
\fBexecute-silent\fR actions.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
\fB# Start HTTP server on port 6266
|
\fB# Start HTTP server on port 6266
|
||||||
@ -748,6 +751,9 @@ e.g.
|
|||||||
|
|
||||||
# Send action to the server
|
# Send action to the server
|
||||||
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
||||||
|
|
||||||
|
# Choose port automatically and export it as $FZF_PORT to the child process
|
||||||
|
fzf --listen --bind 'start:execute-silent:echo $FZF_PORT > /tmp/fzf-port'
|
||||||
\fR
|
\fR
|
||||||
.TP
|
.TP
|
||||||
.B "--version"
|
.B "--version"
|
||||||
|
@ -116,7 +116,7 @@ const usage = `usage: fzf [options]
|
|||||||
--read0 Read input delimited by ASCII NUL characters
|
--read0 Read input delimited by ASCII NUL characters
|
||||||
--print0 Print output delimited by ASCII NUL characters
|
--print0 Print output delimited by ASCII NUL characters
|
||||||
--sync Synchronous search for multi-staged filtering
|
--sync Synchronous search for multi-staged filtering
|
||||||
--listen=HTTP_PORT Start HTTP server to receive actions (POST /)
|
--listen[=HTTP_PORT] Start HTTP server to receive actions (POST /)
|
||||||
--version Display version information and exit
|
--version Display version information and exit
|
||||||
|
|
||||||
Environment variables
|
Environment variables
|
||||||
@ -316,7 +316,7 @@ type Options struct {
|
|||||||
PreviewLabel labelOpts
|
PreviewLabel labelOpts
|
||||||
Unicode bool
|
Unicode bool
|
||||||
Tabstop int
|
Tabstop int
|
||||||
ListenPort int
|
ListenPort *int
|
||||||
ClearOnExit bool
|
ClearOnExit bool
|
||||||
Version bool
|
Version bool
|
||||||
}
|
}
|
||||||
@ -1756,9 +1756,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--tabstop":
|
case "--tabstop":
|
||||||
opts.Tabstop = nextInt(allArgs, &i, "tab stop required")
|
opts.Tabstop = nextInt(allArgs, &i, "tab stop required")
|
||||||
case "--listen":
|
case "--listen":
|
||||||
opts.ListenPort = nextInt(allArgs, &i, "listen port required")
|
port := optionalNumeric(allArgs, &i, 0)
|
||||||
|
opts.ListenPort = &port
|
||||||
case "--no-listen":
|
case "--no-listen":
|
||||||
opts.ListenPort = 0
|
opts.ListenPort = nil
|
||||||
case "--clear":
|
case "--clear":
|
||||||
opts.ClearOnExit = true
|
opts.ClearOnExit = true
|
||||||
case "--no-clear":
|
case "--no-clear":
|
||||||
@ -1849,7 +1850,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
} else if match, value := optString(arg, "--tabstop="); match {
|
} else if match, value := optString(arg, "--tabstop="); match {
|
||||||
opts.Tabstop = atoi(value)
|
opts.Tabstop = atoi(value)
|
||||||
} else if match, value := optString(arg, "--listen="); match {
|
} else if match, value := optString(arg, "--listen="); match {
|
||||||
opts.ListenPort = atoi(value)
|
port := atoi(value)
|
||||||
|
opts.ListenPort = &port
|
||||||
} else if match, value := optString(arg, "--hscroll-off="); match {
|
} else if match, value := optString(arg, "--hscroll-off="); match {
|
||||||
opts.HscrollOff = atoi(value)
|
opts.HscrollOff = atoi(value)
|
||||||
} else if match, value := optString(arg, "--scroll-off="); match {
|
} else if match, value := optString(arg, "--scroll-off="); match {
|
||||||
@ -1879,7 +1881,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
errorExit("tab stop must be a positive integer")
|
errorExit("tab stop must be a positive integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ListenPort < 0 || opts.ListenPort > 65535 {
|
if opts.ListenPort != nil && (*opts.ListenPort < 0 || *opts.ListenPort > 65535) {
|
||||||
errorExit("invalid listen port")
|
errorExit("invalid listen port")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,14 +19,26 @@ const (
|
|||||||
maxContentLength = 1024 * 1024
|
maxContentLength = 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
func startHttpServer(port int, channel chan []*action) error {
|
func startHttpServer(port int, channel chan []*action) (error, int) {
|
||||||
if port == 0 {
|
if port < 0 {
|
||||||
return nil
|
return nil, port
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
|
listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("port not available: %d", port)
|
return fmt.Errorf("port not available: %d", port), port
|
||||||
|
}
|
||||||
|
if port == 0 {
|
||||||
|
addr := listener.Addr().String()
|
||||||
|
parts := strings.SplitN(addr, ":", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return fmt.Errorf("cannot extract port: %s", addr), port
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
port, err = strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return err, port
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -45,7 +57,7 @@ func startHttpServer(port int, channel chan []*action) error {
|
|||||||
listener.Close()
|
listener.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil, port
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we are writing a simplistic HTTP server without using net/http
|
// Here we are writing a simplistic HTTP server without using net/http
|
||||||
|
@ -203,7 +203,7 @@ type Terminal struct {
|
|||||||
padding [4]sizeSpec
|
padding [4]sizeSpec
|
||||||
strong tui.Attr
|
strong tui.Attr
|
||||||
unicode bool
|
unicode bool
|
||||||
listenPort int
|
listenPort *int
|
||||||
borderShape tui.BorderShape
|
borderShape tui.BorderShape
|
||||||
cleanExit bool
|
cleanExit bool
|
||||||
paused bool
|
paused bool
|
||||||
@ -538,7 +538,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
}
|
}
|
||||||
var previewBox *util.EventBox
|
var previewBox *util.EventBox
|
||||||
// We need to start previewer if HTTP server is enabled even when --preview option is not specified
|
// 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 {
|
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) || opts.ListenPort != nil {
|
||||||
previewBox = util.NewEventBox()
|
previewBox = util.NewEventBox()
|
||||||
}
|
}
|
||||||
strongAttr := tui.Bold
|
strongAttr := tui.Bold
|
||||||
@ -694,13 +694,25 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
|
|
||||||
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
||||||
|
|
||||||
if err := startHttpServer(t.listenPort, t.serverChan); err != nil {
|
if t.listenPort != nil {
|
||||||
errorExit(err.Error())
|
err, port := startHttpServer(*t.listenPort, t.serverChan)
|
||||||
|
if err != nil {
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
t.listenPort = &port
|
||||||
}
|
}
|
||||||
|
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) environ() []string {
|
||||||
|
env := os.Environ()
|
||||||
|
if t.listenPort != nil {
|
||||||
|
env = append(env, fmt.Sprintf("FZF_PORT=%d", *t.listenPort))
|
||||||
|
}
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
func borderLines(shape tui.BorderShape) int {
|
func borderLines(shape tui.BorderShape) int {
|
||||||
switch shape {
|
switch shape {
|
||||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||||
@ -2248,6 +2260,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
|||||||
}
|
}
|
||||||
command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
|
command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
|
||||||
cmd := util.ExecCommand(command, false)
|
cmd := util.ExecCommand(command, false)
|
||||||
|
cmd.Env = t.environ()
|
||||||
t.executing.Set(true)
|
t.executing.Set(true)
|
||||||
if !background {
|
if !background {
|
||||||
cmd.Stdin = tui.TtyIn()
|
cmd.Stdin = tui.TtyIn()
|
||||||
@ -2494,17 +2507,17 @@ func (t *Terminal) Loop() {
|
|||||||
_, query := t.Input()
|
_, query := t.Input()
|
||||||
command := t.replacePlaceholder(commandTemplate, false, string(query), items)
|
command := t.replacePlaceholder(commandTemplate, false, string(query), items)
|
||||||
cmd := util.ExecCommand(command, true)
|
cmd := util.ExecCommand(command, true)
|
||||||
|
env := t.environ()
|
||||||
if pwindow != nil {
|
if pwindow != nil {
|
||||||
height := pwindow.Height()
|
height := pwindow.Height()
|
||||||
env := os.Environ()
|
|
||||||
lines := fmt.Sprintf("LINES=%d", height)
|
lines := fmt.Sprintf("LINES=%d", height)
|
||||||
columns := fmt.Sprintf("COLUMNS=%d", pwindow.Width())
|
columns := fmt.Sprintf("COLUMNS=%d", pwindow.Width())
|
||||||
env = append(env, lines)
|
env = append(env, lines)
|
||||||
env = append(env, "FZF_PREVIEW_"+lines)
|
env = append(env, "FZF_PREVIEW_"+lines)
|
||||||
env = append(env, columns)
|
env = append(env, columns)
|
||||||
env = append(env, "FZF_PREVIEW_"+columns)
|
env = append(env, "FZF_PREVIEW_"+columns)
|
||||||
cmd.Env = env
|
|
||||||
}
|
}
|
||||||
|
cmd.Env = env
|
||||||
|
|
||||||
out, _ := cmd.StdoutPipe()
|
out, _ := cmd.StdoutPipe()
|
||||||
cmd.Stderr = cmd.Stdout
|
cmd.Stderr = cmd.Stdout
|
||||||
|
@ -2629,11 +2629,17 @@ class TestGoFZF < TestBase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_listen
|
def test_listen
|
||||||
tmux.send_keys 'seq 10 | fzf --listen 6266', :Enter
|
{ '--listen 6266' => lambda { URI('http://localhost:6266') },
|
||||||
tmux.until { |lines| assert_equal 10, lines.item_count }
|
"--listen --sync --bind 'start:execute-silent:echo $FZF_PORT > /tmp/fzf-port'" =>
|
||||||
Net::HTTP.post(URI('http://localhost:6266'), 'change-query(yo)+reload(seq 100)+change-prompt:hundred> ')
|
lambda { URI("http://localhost:#{File.read('/tmp/fzf-port').chomp}") } }.each do |opts, fn|
|
||||||
tmux.until { |lines| assert_equal 100, lines.item_count }
|
tmux.send_keys "seq 10 | fzf #{opts}", :Enter
|
||||||
tmux.until { |lines| assert_equal 'hundred> yo', lines[-1] }
|
tmux.until { |lines| assert_equal 10, lines.item_count }
|
||||||
|
Net::HTTP.post(fn.call, 'change-query(yo)+reload(seq 100)+change-prompt:hundred> ')
|
||||||
|
tmux.until { |lines| assert_equal 100, lines.item_count }
|
||||||
|
tmux.until { |lines| assert_equal 'hundred> yo', lines[-1] }
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_toggle_alternative_preview_window
|
def test_toggle_alternative_preview_window
|
||||||
|
Loading…
Reference in New Issue
Block a user