mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
Experimental implementation of "reload" action
# Reload input list with different sources seq 10 | fzf --bind 'ctrl-a:reload(seq 100),ctrl-b:reload(seq 1000)' # Reload as you type seq 10 | fzf --bind 'change:reload:seq {q}' --phony # Integration with ripgrep RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case " INITIAL_QUERY="" FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY'" \ fzf --bind "change:reload:$RG_PREFIX {q} || true" \ --ansi --phony --query "$INITIAL_QUERY" Close #751 Close #965 Close #974 Close #1736 Related #1723
This commit is contained in:
parent
11962dabba
commit
78da928727
@ -64,6 +64,13 @@ func (cl *ChunkList) Push(data []byte) bool {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear clears the data
|
||||||
|
func (cl *ChunkList) Clear() {
|
||||||
|
cl.mutex.Lock()
|
||||||
|
cl.chunks = nil
|
||||||
|
cl.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// Snapshot returns immutable snapshot of the ChunkList
|
// Snapshot returns immutable snapshot of the ChunkList
|
||||||
func (cl *ChunkList) Snapshot() ([]*Chunk, int) {
|
func (cl *ChunkList) Snapshot() ([]*Chunk, int) {
|
||||||
cl.mutex.Lock()
|
cl.mutex.Lock()
|
||||||
|
36
src/core.go
36
src/core.go
@ -135,8 +135,9 @@ func Run(opts *Options, revision string) {
|
|||||||
|
|
||||||
// Reader
|
// Reader
|
||||||
streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
|
streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
|
||||||
|
var reader *Reader
|
||||||
if !streamingFilter {
|
if !streamingFilter {
|
||||||
reader := NewReader(func(data []byte) bool {
|
reader = NewReader(func(data []byte) bool {
|
||||||
return chunkList.Push(data)
|
return chunkList.Push(data)
|
||||||
}, eventBox, opts.ReadZero)
|
}, eventBox, opts.ReadZero)
|
||||||
go reader.ReadSource()
|
go reader.ReadSource()
|
||||||
@ -223,6 +224,7 @@ func Run(opts *Options, revision string) {
|
|||||||
// Event coordination
|
// Event coordination
|
||||||
reading := true
|
reading := true
|
||||||
ticks := 0
|
ticks := 0
|
||||||
|
var nextCommand *string
|
||||||
eventBox.Watch(EvtReadNew)
|
eventBox.Watch(EvtReadNew)
|
||||||
for {
|
for {
|
||||||
delay := true
|
delay := true
|
||||||
@ -241,21 +243,41 @@ func Run(opts *Options, revision string) {
|
|||||||
switch evt {
|
switch evt {
|
||||||
|
|
||||||
case EvtReadNew, EvtReadFin:
|
case EvtReadNew, EvtReadFin:
|
||||||
reading = reading && evt == EvtReadNew
|
clearCache := false
|
||||||
|
if evt == EvtReadFin && nextCommand != nil {
|
||||||
|
chunkList.Clear()
|
||||||
|
clearCache = true
|
||||||
|
go reader.restart(*nextCommand)
|
||||||
|
nextCommand = nil
|
||||||
|
} else {
|
||||||
|
reading = reading && evt == EvtReadNew
|
||||||
|
}
|
||||||
snapshot, count := chunkList.Snapshot()
|
snapshot, count := chunkList.Snapshot()
|
||||||
terminal.UpdateCount(count, !reading, value.(bool))
|
terminal.UpdateCount(count, !reading, value.(*string))
|
||||||
if opts.Sync {
|
if opts.Sync {
|
||||||
terminal.UpdateList(PassMerger(&snapshot, opts.Tac))
|
terminal.UpdateList(PassMerger(&snapshot, opts.Tac))
|
||||||
}
|
}
|
||||||
matcher.Reset(snapshot, input(), false, !reading, sort)
|
matcher.Reset(snapshot, input(), false, !reading, sort, clearCache)
|
||||||
|
|
||||||
case EvtSearchNew:
|
case EvtSearchNew:
|
||||||
|
var command *string
|
||||||
switch val := value.(type) {
|
switch val := value.(type) {
|
||||||
case bool:
|
case searchRequest:
|
||||||
sort = val
|
sort = val.sort
|
||||||
|
command = val.command
|
||||||
|
}
|
||||||
|
if command != nil {
|
||||||
|
if reading {
|
||||||
|
reader.terminate()
|
||||||
|
nextCommand = command
|
||||||
|
} else {
|
||||||
|
reading = true
|
||||||
|
chunkList.Clear()
|
||||||
|
go reader.restart(*command)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
snapshot, _ := chunkList.Snapshot()
|
snapshot, _ := chunkList.Snapshot()
|
||||||
matcher.Reset(snapshot, input(), true, !reading, sort)
|
matcher.Reset(snapshot, input(), true, !reading, sort, command != nil)
|
||||||
delay = false
|
delay = false
|
||||||
|
|
||||||
case EvtSearchProgress:
|
case EvtSearchProgress:
|
||||||
|
@ -12,10 +12,11 @@ import (
|
|||||||
|
|
||||||
// MatchRequest represents a search request
|
// MatchRequest represents a search request
|
||||||
type MatchRequest struct {
|
type MatchRequest struct {
|
||||||
chunks []*Chunk
|
chunks []*Chunk
|
||||||
pattern *Pattern
|
pattern *Pattern
|
||||||
final bool
|
final bool
|
||||||
sort bool
|
sort bool
|
||||||
|
clearCache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher is responsible for performing search
|
// Matcher is responsible for performing search
|
||||||
@ -69,7 +70,7 @@ func (m *Matcher) Loop() {
|
|||||||
events.Clear()
|
events.Clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
if request.sort != m.sort {
|
if request.sort != m.sort || request.clearCache {
|
||||||
m.sort = request.sort
|
m.sort = request.sort
|
||||||
m.mergerCache = make(map[string]*Merger)
|
m.mergerCache = make(map[string]*Merger)
|
||||||
clearChunkCache()
|
clearChunkCache()
|
||||||
@ -221,7 +222,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset is called to interrupt/signal the ongoing search
|
// Reset is called to interrupt/signal the ongoing search
|
||||||
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool) {
|
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool, clearCache bool) {
|
||||||
pattern := m.patternBuilder(patternRunes)
|
pattern := m.patternBuilder(patternRunes)
|
||||||
|
|
||||||
var event util.EventType
|
var event util.EventType
|
||||||
@ -230,5 +231,5 @@ func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final
|
|||||||
} else {
|
} else {
|
||||||
event = reqRetry
|
event = reqRetry
|
||||||
}
|
}
|
||||||
m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable})
|
m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable, clearCache})
|
||||||
}
|
}
|
||||||
|
@ -631,13 +631,15 @@ func init() {
|
|||||||
// Backreferences are not supported.
|
// Backreferences are not supported.
|
||||||
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
||||||
executeRegexp = regexp.MustCompile(
|
executeRegexp = regexp.MustCompile(
|
||||||
`(?si):(execute(?:-multi|-silent)?):.+|:(execute(?:-multi|-silent)?)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
`(?si):(execute(?:-multi|-silent)?|reload):.+|:(execute(?:-multi|-silent)?|reload)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeymap(keymap map[int][]action, str string) {
|
func parseKeymap(keymap map[int][]action, str string) {
|
||||||
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
|
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
|
||||||
prefix := ":execute"
|
prefix := ":execute"
|
||||||
if src[len(prefix)] == '-' {
|
if strings.HasPrefix(src, ":reload") {
|
||||||
|
prefix = ":reload"
|
||||||
|
} else if src[len(prefix)] == '-' {
|
||||||
c := src[len(prefix)+1]
|
c := src[len(prefix)+1]
|
||||||
if c == 's' || c == 'S' {
|
if c == 's' || c == 'S' {
|
||||||
prefix += "-silent"
|
prefix += "-silent"
|
||||||
@ -790,6 +792,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
|||||||
} else {
|
} else {
|
||||||
var offset int
|
var offset int
|
||||||
switch t {
|
switch t {
|
||||||
|
case actReload:
|
||||||
|
offset = len("reload")
|
||||||
case actExecuteSilent:
|
case actExecuteSilent:
|
||||||
offset = len("execute-silent")
|
offset = len("execute-silent")
|
||||||
case actExecuteMulti:
|
case actExecuteMulti:
|
||||||
@ -825,6 +829,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
prefix = matches[0][2]
|
prefix = matches[0][2]
|
||||||
}
|
}
|
||||||
switch prefix {
|
switch prefix {
|
||||||
|
case "reload":
|
||||||
|
return actReload
|
||||||
case "execute":
|
case "execute":
|
||||||
return actExecute
|
return actExecute
|
||||||
case "execute-silent":
|
case "execute-silent":
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,11 +18,16 @@ type Reader struct {
|
|||||||
eventBox *util.EventBox
|
eventBox *util.EventBox
|
||||||
delimNil bool
|
delimNil bool
|
||||||
event int32
|
event int32
|
||||||
|
finChan chan bool
|
||||||
|
mutex sync.Mutex
|
||||||
|
exec *exec.Cmd
|
||||||
|
command *string
|
||||||
|
killed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReader returns new Reader object
|
// NewReader returns new Reader object
|
||||||
func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, delimNil bool) *Reader {
|
func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, delimNil bool) *Reader {
|
||||||
return &Reader{pusher, eventBox, delimNil, int32(EvtReady)}
|
return &Reader{pusher, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) startEventPoller() {
|
func (r *Reader) startEventPoller() {
|
||||||
@ -29,9 +36,10 @@ func (r *Reader) startEventPoller() {
|
|||||||
pollInterval := readerPollIntervalMin
|
pollInterval := readerPollIntervalMin
|
||||||
for {
|
for {
|
||||||
if atomic.CompareAndSwapInt32(ptr, int32(EvtReadNew), int32(EvtReady)) {
|
if atomic.CompareAndSwapInt32(ptr, int32(EvtReadNew), int32(EvtReady)) {
|
||||||
r.eventBox.Set(EvtReadNew, true)
|
r.eventBox.Set(EvtReadNew, (*string)(nil))
|
||||||
pollInterval = readerPollIntervalMin
|
pollInterval = readerPollIntervalMin
|
||||||
} else if atomic.LoadInt32(ptr) == int32(EvtReadFin) {
|
} else if atomic.LoadInt32(ptr) == int32(EvtReadFin) {
|
||||||
|
r.finChan <- true
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
pollInterval += readerPollIntervalStep
|
pollInterval += readerPollIntervalStep
|
||||||
@ -46,7 +54,35 @@ func (r *Reader) startEventPoller() {
|
|||||||
|
|
||||||
func (r *Reader) fin(success bool) {
|
func (r *Reader) fin(success bool) {
|
||||||
atomic.StoreInt32(&r.event, int32(EvtReadFin))
|
atomic.StoreInt32(&r.event, int32(EvtReadFin))
|
||||||
r.eventBox.Set(EvtReadFin, success)
|
<-r.finChan
|
||||||
|
|
||||||
|
r.mutex.Lock()
|
||||||
|
ret := r.command
|
||||||
|
if success || r.killed {
|
||||||
|
ret = nil
|
||||||
|
}
|
||||||
|
r.mutex.Unlock()
|
||||||
|
|
||||||
|
r.eventBox.Set(EvtReadFin, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) terminate() {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer func() { r.mutex.Unlock() }()
|
||||||
|
|
||||||
|
r.killed = true
|
||||||
|
if r.exec != nil && r.exec.Process != nil {
|
||||||
|
util.KillCommand(r.exec)
|
||||||
|
} else {
|
||||||
|
os.Stdin.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) restart(command string) {
|
||||||
|
r.event = int32(EvtReady)
|
||||||
|
r.startEventPoller()
|
||||||
|
success := r.readFromCommand(nil, command)
|
||||||
|
r.fin(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadSource reads data from the default command or from standard input
|
// ReadSource reads data from the default command or from standard input
|
||||||
@ -54,12 +90,13 @@ func (r *Reader) ReadSource() {
|
|||||||
r.startEventPoller()
|
r.startEventPoller()
|
||||||
var success bool
|
var success bool
|
||||||
if util.IsTty() {
|
if util.IsTty() {
|
||||||
|
// The default command for *nix requires bash
|
||||||
|
shell := "bash"
|
||||||
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
// The default command for *nix requires bash
|
success = r.readFromCommand(&shell, defaultCommand)
|
||||||
success = r.readFromCommand("bash", defaultCommand)
|
|
||||||
} else {
|
} else {
|
||||||
success = r.readFromCommand("sh", cmd)
|
success = r.readFromCommand(nil, cmd)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
success = r.readFromStdin()
|
success = r.readFromStdin()
|
||||||
@ -102,16 +139,25 @@ func (r *Reader) readFromStdin() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) readFromCommand(shell string, cmd string) bool {
|
func (r *Reader) readFromCommand(shell *string, command string) bool {
|
||||||
listCommand := util.ExecCommandWith(shell, cmd, false)
|
r.mutex.Lock()
|
||||||
out, err := listCommand.StdoutPipe()
|
r.killed = false
|
||||||
|
r.command = &command
|
||||||
|
if shell != nil {
|
||||||
|
r.exec = util.ExecCommandWith(*shell, command, true)
|
||||||
|
} else {
|
||||||
|
r.exec = util.ExecCommand(command, true)
|
||||||
|
}
|
||||||
|
out, err := r.exec.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.mutex.Unlock()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
err = listCommand.Start()
|
err = r.exec.Start()
|
||||||
|
r.mutex.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r.feed(out)
|
r.feed(out)
|
||||||
return listCommand.Wait() == nil
|
return r.exec.Wait() == nil
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ func TestReadFromCommand(t *testing.T) {
|
|||||||
eb := util.NewEventBox()
|
eb := util.NewEventBox()
|
||||||
reader := Reader{
|
reader := Reader{
|
||||||
pusher: func(s []byte) bool { strs = append(strs, string(s)); return true },
|
pusher: func(s []byte) bool { strs = append(strs, string(s)); return true },
|
||||||
|
finChan: make(chan bool, 1),
|
||||||
eventBox: eb,
|
eventBox: eb,
|
||||||
event: int32(EvtReady)}
|
event: int32(EvtReady)}
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ func TestReadFromCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal command
|
// Normal command
|
||||||
reader.fin(reader.readFromCommand("sh", `echo abc && echo def`))
|
reader.fin(reader.readFromCommand(nil, `echo abc && echo def`))
|
||||||
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
|
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
|
||||||
t.Errorf("%s", strs)
|
t.Errorf("%s", strs)
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ func TestReadFromCommand(t *testing.T) {
|
|||||||
reader.startEventPoller()
|
reader.startEventPoller()
|
||||||
|
|
||||||
// Failing command
|
// Failing command
|
||||||
reader.fin(reader.readFromCommand("sh", `no-such-command`))
|
reader.fin(reader.readFromCommand(nil, `no-such-command`))
|
||||||
strs = []string{}
|
strs = []string{}
|
||||||
if len(strs) > 0 {
|
if len(strs) > 0 {
|
||||||
t.Errorf("%s", strs)
|
t.Errorf("%s", strs)
|
||||||
|
@ -102,7 +102,7 @@ type Terminal struct {
|
|||||||
count int
|
count int
|
||||||
progress int
|
progress int
|
||||||
reading bool
|
reading bool
|
||||||
success bool
|
failed *string
|
||||||
jumping jumpMode
|
jumping jumpMode
|
||||||
jumpLabels string
|
jumpLabels string
|
||||||
printer func(string)
|
printer func(string)
|
||||||
@ -228,6 +228,7 @@ const (
|
|||||||
actExecuteMulti // Deprecated
|
actExecuteMulti // Deprecated
|
||||||
actSigStop
|
actSigStop
|
||||||
actTop
|
actTop
|
||||||
|
actReload
|
||||||
)
|
)
|
||||||
|
|
||||||
type placeholderFlags struct {
|
type placeholderFlags struct {
|
||||||
@ -238,6 +239,11 @@ type placeholderFlags struct {
|
|||||||
file bool
|
file bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type searchRequest struct {
|
||||||
|
sort bool
|
||||||
|
command *string
|
||||||
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []action {
|
||||||
actions := make([]action, len(types))
|
actions := make([]action, len(types))
|
||||||
for idx, t := range types {
|
for idx, t := range types {
|
||||||
@ -408,7 +414,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
ansi: opts.Ansi,
|
ansi: opts.Ansi,
|
||||||
tabstop: opts.Tabstop,
|
tabstop: opts.Tabstop,
|
||||||
reading: true,
|
reading: true,
|
||||||
success: true,
|
failed: nil,
|
||||||
jumping: jumpDisabled,
|
jumping: jumpDisabled,
|
||||||
jumpLabels: opts.JumpLabels,
|
jumpLabels: opts.JumpLabels,
|
||||||
printer: opts.Printer,
|
printer: opts.Printer,
|
||||||
@ -440,11 +446,11 @@ func (t *Terminal) Input() []rune {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCount updates the count information
|
// UpdateCount updates the count information
|
||||||
func (t *Terminal) UpdateCount(cnt int, final bool, success bool) {
|
func (t *Terminal) UpdateCount(cnt int, final bool, failedCommand *string) {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
t.count = cnt
|
t.count = cnt
|
||||||
t.reading = !final
|
t.reading = !final
|
||||||
t.success = success
|
t.failed = failedCommand
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
t.reqBox.Set(reqInfo, nil)
|
t.reqBox.Set(reqInfo, nil)
|
||||||
if final {
|
if final {
|
||||||
@ -742,7 +748,9 @@ func (t *Terminal) printInfo() {
|
|||||||
pos = 2
|
pos = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
output := fmt.Sprintf("%d/%d", t.merger.Length(), t.count)
|
found := t.merger.Length()
|
||||||
|
total := util.Max(found, t.count)
|
||||||
|
output := fmt.Sprintf("%d/%d", found, total)
|
||||||
if t.toggleSort {
|
if t.toggleSort {
|
||||||
if t.sort {
|
if t.sort {
|
||||||
output += " +S"
|
output += " +S"
|
||||||
@ -760,16 +768,15 @@ func (t *Terminal) printInfo() {
|
|||||||
if t.progress > 0 && t.progress < 100 {
|
if t.progress > 0 && t.progress < 100 {
|
||||||
output += fmt.Sprintf(" (%d%%)", t.progress)
|
output += fmt.Sprintf(" (%d%%)", t.progress)
|
||||||
}
|
}
|
||||||
if !t.success && t.count == 0 {
|
if t.failed != nil && t.count == 0 {
|
||||||
if len(os.Getenv("FZF_DEFAULT_COMMAND")) > 0 {
|
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||||
output = "[$FZF_DEFAULT_COMMAND failed]"
|
|
||||||
} else {
|
|
||||||
output = "[default command failed - $FZF_DEFAULT_COMMAND required]"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if pos+len(output) <= t.window.Width() {
|
maxWidth := t.window.Width() - pos
|
||||||
t.window.CPrint(tui.ColInfo, 0, output)
|
if len(output) > maxWidth {
|
||||||
|
outputRunes, _ := t.trimRight([]rune(output), maxWidth-2)
|
||||||
|
output = string(outputRunes) + ".."
|
||||||
}
|
}
|
||||||
|
t.window.CPrint(tui.ColInfo, 0, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printHeader() {
|
func (t *Terminal) printHeader() {
|
||||||
@ -1383,7 +1390,7 @@ func (t *Terminal) hasPreviewWindow() bool {
|
|||||||
|
|
||||||
func (t *Terminal) currentItem() *Item {
|
func (t *Terminal) currentItem() *Item {
|
||||||
cnt := t.merger.Length()
|
cnt := t.merger.Length()
|
||||||
if cnt > 0 && cnt > t.cy {
|
if t.cy >= 0 && cnt > 0 && cnt > t.cy {
|
||||||
return t.merger.Get(t.cy).item
|
return t.merger.Get(t.cy).item
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -1508,11 +1515,10 @@ func (t *Terminal) Loop() {
|
|||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
reading := t.reading
|
reading := t.reading
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
if !reading {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(spinnerDuration)
|
time.Sleep(spinnerDuration)
|
||||||
t.reqBox.Set(reqInfo, nil)
|
if reading {
|
||||||
|
t.reqBox.Set(reqInfo, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -1533,7 +1539,7 @@ func (t *Terminal) Loop() {
|
|||||||
// We don't display preview window if no match
|
// We don't display preview window if no match
|
||||||
if request[0] != nil {
|
if request[0] != nil {
|
||||||
command := replacePlaceholder(t.preview.command,
|
command := replacePlaceholder(t.preview.command,
|
||||||
t.ansi, t.delimiter, t.printsep, false, string(t.input), request)
|
t.ansi, t.delimiter, t.printsep, false, string(t.Input()), request)
|
||||||
cmd := util.ExecCommand(command, true)
|
cmd := util.ExecCommand(command, true)
|
||||||
if t.pwindow != nil {
|
if t.pwindow != nil {
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
@ -1673,6 +1679,10 @@ func (t *Terminal) Loop() {
|
|||||||
|
|
||||||
looping := true
|
looping := true
|
||||||
for looping {
|
for looping {
|
||||||
|
var newCommand *string
|
||||||
|
changed := false
|
||||||
|
queryChanged := false
|
||||||
|
|
||||||
event := t.tui.GetChar()
|
event := t.tui.GetChar()
|
||||||
|
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
@ -1754,9 +1764,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
case actToggleSort:
|
case actToggleSort:
|
||||||
t.sort = !t.sort
|
t.sort = !t.sort
|
||||||
t.eventBox.Set(EvtSearchNew, t.sort)
|
changed = true
|
||||||
t.mutex.Unlock()
|
|
||||||
return false
|
|
||||||
case actPreviewUp:
|
case actPreviewUp:
|
||||||
if t.hasPreviewWindow() {
|
if t.hasPreviewWindow() {
|
||||||
scrollPreview(-1)
|
scrollPreview(-1)
|
||||||
@ -2025,10 +2033,24 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case actReload:
|
||||||
|
t.failed = nil
|
||||||
|
|
||||||
|
valid, list := t.buildPlusList(a.a, false)
|
||||||
|
// If the command template has {q}, we run the command even when the
|
||||||
|
// query string is empty.
|
||||||
|
if !valid {
|
||||||
|
_, query := hasPreviewFlags(a.a)
|
||||||
|
valid = query
|
||||||
|
}
|
||||||
|
if valid {
|
||||||
|
command := replacePlaceholder(a.a,
|
||||||
|
t.ansi, t.delimiter, t.printsep, false, string(t.input), list)
|
||||||
|
newCommand = &command
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
changed := false
|
|
||||||
mapkey := event.Type
|
mapkey := event.Type
|
||||||
if t.jumping == jumpDisabled {
|
if t.jumping == jumpDisabled {
|
||||||
actions := t.keymap[mapkey]
|
actions := t.keymap[mapkey]
|
||||||
@ -2042,8 +2064,9 @@ func (t *Terminal) Loop() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.truncateQuery()
|
t.truncateQuery()
|
||||||
changed = string(previousInput) != string(t.input)
|
queryChanged = string(previousInput) != string(t.input)
|
||||||
if onChanges, prs := t.keymap[tui.Change]; changed && prs {
|
changed = changed || queryChanged
|
||||||
|
if onChanges, prs := t.keymap[tui.Change]; queryChanged && prs {
|
||||||
if !doActions(onChanges, tui.Change) {
|
if !doActions(onChanges, tui.Change) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -2061,7 +2084,7 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqList)
|
req(reqList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if queryChanged {
|
||||||
if t.isPreviewEnabled() {
|
if t.isPreviewEnabled() {
|
||||||
_, q := hasPreviewFlags(t.preview.command)
|
_, q := hasPreviewFlags(t.preview.command)
|
||||||
if q {
|
if q {
|
||||||
@ -2070,14 +2093,14 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed || t.cx != previousCx {
|
if queryChanged || t.cx != previousCx {
|
||||||
req(reqPrompt)
|
req(reqPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
||||||
|
|
||||||
if changed {
|
if changed || newCommand != nil {
|
||||||
t.eventBox.Set(EvtSearchNew, t.sort)
|
t.eventBox.Set(EvtSearchNew, searchRequest{sort: t.sort, command: newCommand})
|
||||||
}
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
t.reqBox.Set(event, nil)
|
t.reqBox.Set(event, nil)
|
||||||
|
Loading…
Reference in New Issue
Block a user