Compare commits

..
Author SHA1 Message Date
Junegunn Choi 314cbe1be7 Fix --info inline-right:PREFIX not clearing previous output
Close #4953
2026-11-11 19:26:21 +09:00
Junegunn Choi c7bf2b8eea 0.75.0 2026-11-07 20:58:45 +09:00
Junegunn Choi 69114661a6 Fix data races on preview placeholders and merger.final
- Capture lastAction and prompt in preview request, like query and env.
  Preview goroutine read t.lastAction and t.promptString while the main
  loop wrote them.
- Do not write merger.final on a cached merger. Cache hit already
  requires the same value, and the terminal may be reading it.

Close #4947
2026-11-07 20:37:20 +09:00
Junegunn ChoiandMarshall Porter 96028e2a54 Add MSI installers for Windows and offer them through winget (#4948)
Co-authored-by: Marshall Porter <meoporter@gmail.com>
2026-11-06 10:45:01 +09:00
11 changed files with 58 additions and 13 deletions

No files matched your search

+5
View File
@@ -1,6 +1,10 @@
CHANGELOG
=========
0.75.1
------
- Fixed `--info inline-right:PREFIX` leaving part of the previous output when the info gets shorter (#4953)
0.75.0
------
- Added `{change,transform,bg-transform}-info-command` actions (#4951) (@casperdcl)
@@ -30,6 +34,7 @@ CHANGELOG
```
- Fixed `--no-sort` being ignored in filter mode with `--sync`, `--tac`, or `--bench` (#4940) (@NavpreetSSidhu)
- Fixed `--walker=follow` skipping a symlink in one `--walker-root` when its target is an ancestor of another root (#4944) (@mabunemeh)
- Fixed data races in the preview and the search result cache (#4947)
- Fixed O(n^2) action parsing in `--listen`. A single ~1 MB request could block the server for ~30 s (@zachariah-mithani, AploSec)
- Windows
- Fixed `--with-shell` on Windows not detecting cmd, PowerShell, and pwsh, which made fzf quote commands and entries for a POSIX shell (#4936) (@vimkim)
+3 -3
View File
@@ -22,7 +22,7 @@ triggered by a tag push.
3. Verify file consistency, sign the tag, and push the tag.
```sh
make tag VERSION=0.74.5
make tag VERSION=0.75.0
```
`make tag` runs `prerelease` first (checks that the version
@@ -54,8 +54,8 @@ The release stays a draft. Do not push `master` until it is published.
- Publish without MSIs and submit to winget by hand:
```sh
gh release edit v0.74.5 --draft=false
gh workflow run winget.yml -f release-tag=v0.74.5
gh release edit v0.75.0 --draft=false
gh workflow run winget.yml -f release-tag=v0.75.0
```
## Testing the workflow
+1 -1
View File
@@ -2,7 +2,7 @@
set -u
version=0.74.5
version=0.75.0
auto_completion=
key_bindings=
update_config=2
+1 -1
View File
@@ -1,4 +1,4 @@
$version="0.74.5"
$version="0.75.0"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/junegunn/fzf/src/protector"
)
var version = "0.74"
var version = "0.75"
var revision = "devel"
//go:embed shell/key-bindings.bash
+1 -1
View File
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf\-tmux 1 "Oct 2026" "fzf 0.74.5" "fzf\-tmux - open fzf in tmux split pane"
.TH fzf\-tmux 1 "Nov 2026" "fzf 0.75.0" "fzf\-tmux - open fzf in tmux split pane"
.SH NAME
fzf\-tmux - open fzf in tmux split pane
+1 -1
View File
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf 1 "Oct 2026" "fzf 0.74.5" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Nov 2026" "fzf 0.75.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
+5 -1
View File
@@ -137,13 +137,17 @@ func (m *Matcher) Loop() {
m.scanMutex.Lock()
result = m.scan(request)
m.scanMutex.Unlock()
// Not on a cached merger, which the terminal may be reading. It
// already has the same value.
if result.merger != nil {
result.merger.final = request.final
}
}
if !result.cancelled {
if result.cacheable() {
m.mergerCache[patternString] = result
}
result.merger.final = request.final
m.eventBox.Set(EvtSearchFin, result)
}
}
+24 -4
View File
@@ -843,6 +843,8 @@ type previewRequest struct {
list [3][]*Item // current, select, and all matched items
env []string
query string
lastAction actionType
prompt string
}
type previewResult struct {
@@ -3662,7 +3664,10 @@ func (t *Terminal) printInfoImpl() {
pos++
}
} else {
pos = max(pos, t.window.Width()-outputLen-util.StringWidth(t.infoPrefix)-1)
move(line, pos, false)
newPos := max(pos, t.window.Width()-outputLen-util.StringWidth(t.infoPrefix)-1)
t.window.Print(strings.Repeat(" ", newPos-pos))
pos = newPos
printInfoPrefix()
}
}
@@ -6458,6 +6463,8 @@ func (t *Terminal) Loop() error {
var commandTemplate string
var env []string
var query string
var lastAction actionType
var prompt string
initialOffset := 0
t.previewBox.Wait(func(events *util.Events) {
for req, value := range *events {
@@ -6472,6 +6479,8 @@ func (t *Terminal) Loop() error {
items = request.list
env = request.env
query = request.query
lastAction = request.lastAction
prompt = request.prompt
requested = true
}
}
@@ -6486,7 +6495,18 @@ func (t *Terminal) Loop() error {
version++
// We don't display preview window if no match
if items[0] != nil {
command, tempFiles := t.replacePlaceholder(commandTemplate, false, query, items)
// Not t.replacePlaceholder, which reads fields the main loop writes
command, tempFiles := replacePlaceholder(replacePlaceholderParams{
template: commandTemplate,
stripAnsi: t.ansi,
delimiter: t.delimiter,
printsep: t.printsep,
query: query,
allItems: items,
lastAction: lastAction,
prompt: prompt,
executor: t.executor,
})
cmd := t.executor.ExecCommand(command, true)
cmd.Env = env
@@ -6619,7 +6639,7 @@ func (t *Terminal) Loop() error {
if len(command) > 0 && t.canPreview() {
_, list := t.buildPlusList(command, false)
t.cancelPreview()
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, t.evaluateScrollOffset(), list, t.environForPreview(), string(t.input)})
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, t.evaluateScrollOffset(), list, t.environForPreview(), string(t.input), t.lastAction, t.promptString})
}
}
@@ -7234,7 +7254,7 @@ func (t *Terminal) Loop() error {
if valid {
t.cancelPreview()
t.previewBox.Set(reqPreviewEnqueue,
previewRequest{t.previewOpts.command, t.evaluateScrollOffset(), list, t.environForPreview(), string(t.input)})
previewRequest{t.previewOpts.command, t.evaluateScrollOffset(), list, t.environForPreview(), string(t.input), t.lastAction, t.promptString})
}
} else {
// Discard the preview content so that it won't accidentally appear
+7
View File
@@ -1792,6 +1792,13 @@ class TestCore < TestInteractive
tmux.until { assert_match(%r{ 0/100000}, it[-1]) }
end
def test_info_inline_right_prefix_clearance
tmux.send_keys "seq 100000 | #{FZF} --info 'inline-right:xx '", :Enter
tmux.until { assert_match(%r{xx 100000/100000}, it[-1]) }
tmux.send_keys 'x'
tmux.until { assert_match(%r{ xx 0/100000}, it[-1]) }
end
def test_info_command
tmux.send_keys(%(seq 10000 | #{FZF} --separator x --info-command 'echo -e "--\\x1b[33m$FZF_POS\\x1b[m/$FZF_INFO--"'), :Enter)
tmux.until { assert_match(%r{^ --1/10000/10000-- xx}, it[-2]) }
+9
View File
@@ -640,6 +640,15 @@ class TestPreview < TestInteractive
end
end
def test_preview_action_and_prompt
tmux.send_keys %(seq 100 | #{FZF} --preview 'echo [$FZF_ACTION/$FZF_PROMPT/{q}]' --bind 'space:change-prompt(foo> )'), :Enter
tmux.until { |lines| assert lines.any_include?('[start/> /]') }
tmux.send_keys '1'
tmux.until { |lines| assert lines.any_include?('[char/> /1]') }
tmux.send_keys :Space, '2'
tmux.until { |lines| assert lines.any_include?('[char/foo> /12]') }
end
def test_preview_wrap_sign_between_ansi_fragments
tmux.send_keys %(seq 1 | #{FZF} --preview 'echo -e "\\x1b[33m1234567890 \\x1b[mhello"; echo -e "\\x1b[33m1234567890 \\x1b[mhello"' --preview-window 10,wrap-word), :Enter
tmux.until do |lines|