summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--man/man1/fzf.15
-rw-r--r--src/chunklist.go18
-rw-r--r--src/constants.go1
-rw-r--r--src/core.go24
-rw-r--r--src/options.go144
-rw-r--r--src/reader.go7
-rw-r--r--src/reader_test.go2
-rw-r--r--src/terminal.go19
8 files changed, 143 insertions, 77 deletions
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 8d10d389..c42fe668 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -259,8 +259,11 @@ Maximum number of entries in the history file (default: 1000). The file is
automatically truncated when the number of the lines exceeds the value.
.TP
.BI "--header-file=" "FILE"
-The content of the file will be printed as the "sticky" header. The file can
+The content of the file will be printed as the sticky header. The file can
span multiple lines and can contain ANSI color codes.
+.TP
+.BI "--header-lines=" "N"
+The first N lines of the input are treated as the sticky header.
.SS Scripting
.TP
.BI "-q, --query=" "STR"
diff --git a/src/chunklist.go b/src/chunklist.go
index 52084f2f..ee52d321 100644
--- a/src/chunklist.go
+++ b/src/chunklist.go
@@ -26,8 +26,13 @@ func NewChunkList(trans ItemBuilder) *ChunkList {
trans: trans}
}
-func (c *Chunk) push(trans ItemBuilder, data *string, index int) {
- *c = append(*c, trans(data, index))
+func (c *Chunk) push(trans ItemBuilder, data *string, index int) bool {
+ item := trans(data, index)
+ if item != nil {
+ *c = append(*c, item)
+ return true
+ }
+ return false
}
// IsFull returns true if the Chunk is full
@@ -48,7 +53,7 @@ func CountItems(cs []*Chunk) int {
}
// Push adds the item to the list
-func (cl *ChunkList) Push(data string) {
+func (cl *ChunkList) Push(data string) bool {
cl.mutex.Lock()
defer cl.mutex.Unlock()
@@ -57,8 +62,11 @@ func (cl *ChunkList) Push(data string) {
cl.chunks = append(cl.chunks, &newChunk)
}
- cl.lastChunk().push(cl.trans, &data, cl.count)
- cl.count++
+ if cl.lastChunk().push(cl.trans, &data, cl.count) {
+ cl.count++
+ return true
+ }
+ return false
}
// Snapshot returns immutable snapshot of the ChunkList
diff --git a/src/constants.go b/src/constants.go
index 73ba451e..26929079 100644
--- a/src/constants.go
+++ b/src/constants.go
@@ -44,5 +44,6 @@ const (
EvtSearchNew
EvtSearchProgress
EvtSearchFin
+ EvtHeader
EvtClose
)
diff --git a/src/core.go b/src/core.go
index c7277080..e38908a8 100644
--- a/src/core.go
+++ b/src/core.go
@@ -44,6 +44,7 @@ Reader -> EvtReadNew -> Matcher (restart)
Terminal -> EvtSearchNew:bool -> Matcher (restart)
Matcher -> EvtSearchProgress -> Terminal (update info)
Matcher -> EvtSearchFin -> Terminal (update list)
+Matcher -> EvtHeader -> Terminal (update header)
*/
// Run starts fzf
@@ -83,8 +84,14 @@ func Run(opts *Options) {
// Chunk list
var chunkList *ChunkList
+ header := make([]string, 0, opts.HeaderLines)
if len(opts.WithNth) == 0 {
chunkList = NewChunkList(func(data *string, index int) *Item {
+ if len(header) < opts.HeaderLines {
+ header = append(header, *data)
+ eventBox.Set(EvtHeader, header)
+ return nil
+ }
data, colors := ansiProcessor(data)
return &Item{
text: data,
@@ -94,6 +101,11 @@ func Run(opts *Options) {
})
} else {
chunkList = NewChunkList(func(data *string, index int) *Item {
+ if len(header) < opts.HeaderLines {
+ header = append(header, *data)
+ eventBox.Set(EvtHeader, header)
+ return nil
+ }
tokens := Tokenize(data, opts.Delimiter)
trans := Transform(tokens, opts.WithNth)
item := Item{
@@ -113,7 +125,9 @@ func Run(opts *Options) {
// Reader
streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
if !streamingFilter {
- reader := Reader{func(str string) { chunkList.Push(str) }, eventBox, opts.ReadZero}
+ reader := Reader{func(str string) bool {
+ return chunkList.Push(str)
+ }, eventBox, opts.ReadZero}
go reader.ReadSource()
}
@@ -134,11 +148,12 @@ func Run(opts *Options) {
if streamingFilter {
reader := Reader{
- func(str string) {
+ func(str string) bool {
item := chunkList.trans(&str, 0)
- if pattern.MatchItem(item) {
+ if item != nil && pattern.MatchItem(item) {
fmt.Println(*item.text)
}
+ return false
}, eventBox, opts.ReadZero}
reader.ReadSource()
} else {
@@ -206,6 +221,9 @@ func Run(opts *Options) {
terminal.UpdateProgress(val)
}
+ case EvtHeader:
+ terminal.UpdateHeader(value.([]string), opts.HeaderLines)
+
case EvtSearchFin:
switch val := value.(type) {
case *Merger:
diff --git a/src/options.go b/src/options.go
index e789d4e4..d8b2bd87 100644
--- a/src/options.go
+++ b/src/options.go
@@ -46,6 +46,7 @@ const usage = `usage: fzf [options]
--history=FILE History file
--history-size=N Maximum number of history entries (default: 1000)
--header-file=FILE The file whose content to be printed as header
+ --header-lines=N The first N lines of the input are treated as header
Scripting
-q, --query=STR Start the finder with the given query
@@ -94,38 +95,39 @@ const (
// Options stores the values of command-line options
type Options struct {
- Mode Mode
- Case Case
- Nth []Range
- WithNth []Range
- Delimiter *regexp.Regexp
- Sort int
- Tac bool
- Tiebreak tiebreak
- Multi bool
- Ansi bool
- Mouse bool
- Theme *curses.ColorTheme
- Black bool
- Reverse bool
- Cycle bool
- Hscroll bool
- InlineInfo bool
- Prompt string
- Query string
- Select1 bool
- Exit0 bool
- Filter *string
- ToggleSort bool
- Expect map[int]string
- Keymap map[int]actionType
- Execmap map[int]string
- PrintQuery bool
- ReadZero bool
- Sync bool
- History *History
- Header []string
- Version bool
+ Mode Mode
+ Case Case
+ Nth []Range
+ WithNth []Range
+ Delimiter *regexp.Regexp
+ Sort int
+ Tac bool
+ Tiebreak tiebreak
+ Multi bool
+ Ansi bool
+ Mouse bool
+ Theme *curses.ColorTheme
+ Black bool
+ Reverse bool
+ Cycle bool
+ Hscroll bool
+ InlineInfo bool
+ Prompt string
+ Query string
+ Select1 bool
+ Exit0 bool
+ Filter *string
+ ToggleSort bool
+ Expect map[int]string
+ Keymap map[int]actionType
+ Execmap map[int]string
+ PrintQuery bool
+ ReadZero bool
+ Sync bool
+ History *History
+ Header []string
+ HeaderLines int
+ Version bool
}
func defaultTheme() *curses.ColorTheme {
@@ -137,38 +139,39 @@ func defaultTheme() *curses.ColorTheme {
func defaultOptions() *Options {
return &Options{
- Mode: ModeFuzzy,
- Case: CaseSmart,
- Nth: make([]Range, 0),
- WithNth: make([]Range, 0),
- Delimiter: nil,
- Sort: 1000,
- Tac: false,
- Tiebreak: byLength,
- Multi: false,
- Ansi: false,
- Mouse: true,
- Theme: defaultTheme(),
- Black: false,
- Reverse: false,
- Cycle: false,
- Hscroll: true,
- InlineInfo: false,
- Prompt: "> ",
- Query: "",
- Select1: false,
- Exit0: false,
- Filter: nil,
- ToggleSort: false,
- Expect: make(map[int]string),
- Keymap: defaultKeymap(),
- Execmap: make(map[int]string),
- PrintQuery: false,
- ReadZero: false,
- Sync: false,
- History: nil,
- Header: make([]string, 0),
- Version: false}
+ Mode: ModeFuzzy,
+ Case: CaseSmart,
+ Nth: make([]Range, 0),
+ WithNth: make([]Range, 0),
+ Delimiter: nil,
+ Sort: 1000,
+ Tac: false,
+ Tiebreak: byLength,
+ Multi: false,
+ Ansi: false,
+ Mouse: true,
+ Theme: defaultTheme(),
+ Black: false,
+ Reverse: false,
+ Cycle: false,
+ Hscroll: true,
+ InlineInfo: false,
+ Prompt: "> ",
+ Query: "",
+ Select1: false,
+ Exit0: false,
+ Filter: nil,
+ ToggleSort: false,
+ Expect: make(map[int]string),
+ Keymap: defaultKeymap(),
+ Execmap: make(map[int]string),
+ PrintQuery: false,
+ ReadZero: false,
+ Sync: false,
+ History: nil,
+ Header: make([]string, 0),
+ HeaderLines: 0,
+ Version: false}
}
func help(ok int) {
@@ -724,9 +727,18 @@ func parseOptions(opts *Options, allArgs []string) {
setHistory(nextString(allArgs, &i, "history file path required"))
case "--history-size":
setHistoryMax(nextInt(allArgs, &i, "history max size required"))
+ case "--no-header-file":
+ opts.Header = []string{}
+ case "--no-header-lines":
+ opts.HeaderLines = 0
case "--header-file":
opts.Header = readHeaderFile(
nextString(allArgs, &i, "header file name required"))
+ opts.HeaderLines = 0
+ case "--header-lines":
+ opts.Header = []string{}
+ opts.HeaderLines = atoi(
+ nextString(allArgs, &i, "number of header lines required"))
case "--version":
opts.Version = true
default:
@@ -762,6 +774,10 @@ func parseOptions(opts *Options, allArgs []string) {
setHistoryMax(atoi(value))
} else if match, value := optString(arg, "--header-file="); match {
opts.Header = readHeaderFile(value)
+ opts.HeaderLines = 0
+ } else if match, value := optString(arg, "--header-lines="); match {
+ opts.Header = []string{}
+ opts.HeaderLines = atoi(value)
} else {
errorExit("unknown option: " + arg)
}
diff --git a/src/reader.go b/src/reader.go
index 356c2db8..aab8b02a 100644
--- a/src/reader.go
+++ b/src/reader.go
@@ -11,7 +11,7 @@ import (
// Reader reads from command or standard input
type Reader struct {
- pusher func(string)
+ pusher func(string) bool
eventBox *util.EventBox
delimNil bool
}
@@ -43,8 +43,9 @@ func (r *Reader) feed(src io.Reader) {
if err == nil {
line = line[:len(line)-1]
}
- r.pusher(line)
- r.eventBox.Set(EvtReadNew, nil)
+ if r.pusher(line) {
+ r.eventBox.Set(EvtReadNew, nil)
+ }
}
if err != nil {
break
diff --git a/src/reader_test.go b/src/reader_test.go
index 144a3ff1..00b9e337 100644
--- a/src/reader_test.go
+++ b/src/reader_test.go
@@ -10,7 +10,7 @@ func TestReadFromCommand(t *testing.T) {
strs := []string{}
eb := util.NewEventBox()
reader := Reader{
- pusher: func(s string) { strs = append(strs, s) },
+ pusher: func(s string) bool { strs = append(strs, s); return true },
eventBox: eb}
// Check EventBox
diff --git a/src/terminal.go b/src/terminal.go
index 43d2d8c0..844574a1 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -79,6 +79,7 @@ var _runeWidths = make(map[rune]int)
const (
reqPrompt util.EventType = iota
reqInfo
+ reqHeader
reqList
reqRefresh
reqRedraw
@@ -231,6 +232,22 @@ func (t *Terminal) UpdateCount(cnt int, final bool) {
}
}
+// UpdateHeader updates the header
+func (t *Terminal) UpdateHeader(header []string, lines int) {
+ t.mutex.Lock()
+ t.header = make([]string, lines)
+ copy(t.header, header)
+ if !t.reverse {
+ reversed := make([]string, lines)
+ for idx, str := range t.header {
+ reversed[lines-idx-1] = str
+ }
+ t.header = reversed
+ }
+ t.mutex.Unlock()
+ t.reqBox.Set(reqHeader, nil)
+}
+
// UpdateProgress updates the search progress
func (t *Terminal) UpdateProgress(progress float32) {
t.mutex.Lock()
@@ -686,6 +703,8 @@ func (t *Terminal) Loop() {
t.printInfo()
case reqList:
t.printList()
+ case reqHeader:
+ t.printHeader()
case reqRefresh:
t.suppress = false
case reqRedraw: