Compare commits

...

3 Commits

Author SHA1 Message Date
pull[bot]
3474dbaa83
Merge 56fef7c8df into a30181e240 2025-01-13 08:38:49 +00:00
Junegunn Choi
56fef7c8df
Simplify nth comparison when reusing transformed tokens 2025-01-13 17:37:50 +09:00
Junegunn Choi
ba0935c71f
Fix change-nth
* Proper clean-up of caches
* Force rerender list after the action
2025-01-13 12:45:01 +09:00
6 changed files with 34 additions and 12 deletions

View File

@ -192,11 +192,12 @@ func Run(opts *Options) (int, error) {
} }
nth := opts.Nth nth := opts.Nth
nthRevision := 0
patternCache := make(map[string]*Pattern) patternCache := make(map[string]*Pattern)
patternBuilder := func(runes []rune) *Pattern { patternBuilder := func(runes []rune) *Pattern {
return BuildPattern(cache, patternCache, return BuildPattern(cache, patternCache,
opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos, opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
opts.Filter == nil, nth, opts.Delimiter, runes) opts.Filter == nil, nth, opts.Delimiter, nthRevision, runes)
} }
inputRevision := revision{} inputRevision := revision{}
snapshotRevision := revision{} snapshotRevision := revision{}
@ -378,8 +379,10 @@ func Run(opts *Options) (int, error) {
if val.nth != nil { if val.nth != nil {
// Change nth and clear caches // Change nth and clear caches
nth = *val.nth nth = *val.nth
nthRevision++
patternCache = make(map[string]*Pattern) patternCache = make(map[string]*Pattern)
inputRevision.bumpMajor() cache.Clear()
inputRevision.bumpMinor()
} }
if command != nil { if command != nil {
useSnapshot = val.sync useSnapshot = val.sync

View File

@ -6,10 +6,17 @@ import (
"github.com/junegunn/fzf/src/util" "github.com/junegunn/fzf/src/util"
) )
type transformed struct {
// Because nth can be changed dynamically by change-nth action, we need to
// keep the revision number at the time of transformation.
revision int
tokens []Token
}
// Item represents each input line. 56 bytes. // Item represents each input line. 56 bytes.
type Item struct { type Item struct {
text util.Chars // 32 = 24 + 1 + 1 + 2 + 4 text util.Chars // 32 = 24 + 1 + 1 + 2 + 4
transformed *[]Token // 8 transformed *transformed // 8
origText *[]byte // 8 origText *[]byte // 8
colors *[]ansiOffset // 8 colors *[]ansiOffset // 8
} }

View File

@ -60,6 +60,7 @@ type Pattern struct {
cacheKey string cacheKey string
delimiter Delimiter delimiter Delimiter
nth []Range nth []Range
revision int
procFun map[termType]algo.Algo procFun map[termType]algo.Algo
cache *ChunkCache cache *ChunkCache
} }
@ -72,7 +73,7 @@ func init() {
// BuildPattern builds Pattern object from the given arguments // BuildPattern builds Pattern object from the given arguments
func BuildPattern(cache *ChunkCache, patternCache map[string]*Pattern, fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case, normalize bool, forward bool, func BuildPattern(cache *ChunkCache, patternCache map[string]*Pattern, fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case, normalize bool, forward bool,
withPos bool, cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern { withPos bool, cacheable bool, nth []Range, delimiter Delimiter, revision int, runes []rune) *Pattern {
var asString string var asString string
if extended { if extended {
@ -140,6 +141,7 @@ func BuildPattern(cache *ChunkCache, patternCache map[string]*Pattern, fuzzy boo
sortable: sortable, sortable: sortable,
cacheable: cacheable, cacheable: cacheable,
nth: nth, nth: nth,
revision: revision,
delimiter: delimiter, delimiter: delimiter,
cache: cache, cache: cache,
procFun: make(map[termType]algo.Algo)} procFun: make(map[termType]algo.Algo)}
@ -393,12 +395,15 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
func (p *Pattern) transformInput(item *Item) []Token { func (p *Pattern) transformInput(item *Item) []Token {
if item.transformed != nil { if item.transformed != nil {
return *item.transformed transformed := *item.transformed
if transformed.revision == p.revision {
return transformed.tokens
}
} }
tokens := Tokenize(item.text.ToString(), p.delimiter) tokens := Tokenize(item.text.ToString(), p.delimiter)
ret := Transform(tokens, p.nth) ret := Transform(tokens, p.nth)
item.transformed = &ret item.transformed = &transformed{p.revision, ret}
return ret return ret
} }

View File

@ -68,7 +68,7 @@ func buildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case,
withPos bool, cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern { withPos bool, cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern {
return BuildPattern(NewChunkCache(), make(map[string]*Pattern), return BuildPattern(NewChunkCache(), make(map[string]*Pattern),
fuzzy, fuzzyAlgo, extended, caseMode, normalize, forward, fuzzy, fuzzyAlgo, extended, caseMode, normalize, forward,
withPos, cacheable, nth, delimiter, runes) withPos, cacheable, nth, delimiter, 0, runes)
} }
func TestExact(t *testing.T) { func TestExact(t *testing.T) {
@ -135,12 +135,12 @@ func TestOrigTextAndTransformed(t *testing.T) {
chunk.items[0] = Item{ chunk.items[0] = Item{
text: util.ToChars([]byte("junegunn")), text: util.ToChars([]byte("junegunn")),
origText: &origBytes, origText: &origBytes,
transformed: &trans} transformed: &transformed{pattern.revision, trans}}
pattern.extended = extended pattern.extended = extended
matches := pattern.matchChunk(&chunk, nil, slab) // No cache matches := pattern.matchChunk(&chunk, nil, slab) // No cache
if !(matches[0].item.text.ToString() == "junegunn" && if !(matches[0].item.text.ToString() == "junegunn" &&
string(*matches[0].item.origText) == "junegunn.choi" && string(*matches[0].item.origText) == "junegunn.choi" &&
reflect.DeepEqual(*matches[0].item.transformed, trans)) { reflect.DeepEqual((*matches[0].item.transformed).tokens, trans)) {
t.Error("Invalid match result", matches) t.Error("Invalid match result", matches)
} }
@ -148,7 +148,7 @@ func TestOrigTextAndTransformed(t *testing.T) {
if !(match.item.text.ToString() == "junegunn" && if !(match.item.text.ToString() == "junegunn" &&
string(*match.item.origText) == "junegunn.choi" && string(*match.item.origText) == "junegunn.choi" &&
offsets[0][0] == 0 && offsets[0][1] == 5 && offsets[0][0] == 0 && offsets[0][1] == 5 &&
reflect.DeepEqual(*match.item.transformed, trans)) { reflect.DeepEqual((*match.item.transformed).tokens, trans)) {
t.Error("Invalid match result", match, offsets, extended) t.Error("Invalid match result", match, offsets, extended)
} }
if !((*pos)[0] == 4 && (*pos)[1] == 0) { if !((*pos)[0] == 4 && (*pos)[1] == 0) {

View File

@ -1626,6 +1626,10 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
return screenWidth, screenHeight, marginInt, paddingInt return screenWidth, screenHeight, marginInt, paddingInt
} }
func (t *Terminal) forceRerenderList() {
t.prevLines = make([]itemLine, len(t.prevLines))
}
func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) { func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
t.forcePreview = forcePreview t.forcePreview = forcePreview
screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding() screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
@ -4639,6 +4643,7 @@ func (t *Terminal) Loop() error {
if len(tokens) > 1 { if len(tokens) > 1 {
a.a = strings.Join(append(tokens[1:], tokens[0]), "|") a.a = strings.Join(append(tokens[1:], tokens[0]), "|")
} }
t.forceRerenderList()
case actChangeQuery: case actChangeQuery:
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)
@ -5101,7 +5106,7 @@ func (t *Terminal) Loop() error {
req(reqList) req(reqList)
case actToggleHscroll: case actToggleHscroll:
// Force re-rendering of the list // Force re-rendering of the list
t.prevLines = make([]itemLine, len(t.prevLines)) t.forceRerenderList()
t.hscroll = !t.hscroll t.hscroll = !t.hscroll
req(reqList) req(reqList)
case actTrackCurrent: case actTrackCurrent:

View File

@ -3721,10 +3721,12 @@ class TestGoFZF < TestBase
def test_change_nth def test_change_nth
input = [ input = [
*[''] * 1000,
'foo bar bar bar bar', 'foo bar bar bar bar',
'foo foo bar bar bar', 'foo foo bar bar bar',
'foo foo foo bar bar', 'foo foo foo bar bar',
'foo foo foo foo bar' 'foo foo foo foo bar',
*[''] * 1000
] ]
writelines(input) writelines(input)
tmux.send_keys %(#{FZF} -qfoo -n1 --bind 'space:change-nth:2|3|4|5|' < #{tempname}), :Enter tmux.send_keys %(#{FZF} -qfoo -n1 --bind 'space:change-nth:2|3|4|5|' < #{tempname}), :Enter