peco

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2014 License: MIT Imports: 18 Imported by: 0

README

peco

Simplistic interactive filtering tool

Description

peco is based on percol. The idea is that percol was darn useful, but I wanted a tool that was a single binary. peco is written in Go, and as of this writing only implements the basic filtering feature (mainly because that's the only thing I use -- you're welcome to send me pull requests to make peco more compatible with percol).

peco can be a great tool to filter stuff like logs, process stats, find files, because unlike grep, you can type as you think and look through the current results.

Demo

Demos speak more than a thousand words! Here's me looking for a process on my mac. As you can see, you can page through your results, and you can keep changing the query:

optimized

Here's me trying to figure out which file to open:

optimized

When you combine tools like zsh, peco, and ghq, you can make managing/moving around your huge dev area a piece of cake! (this example doesn't use zsh functions so you can see what I'm doing)

optimized

Features

Search results are filtered as you type. This is great to drill down to the line you are looking for

Multiple terms turn the query into an "AND" query:

optimized

When you find that line that you want, press enter, and the resulting line is printed to stdout, which allows you to pipe it to other tools

Select Matchers

Different types of matchers are available. Default is case-insensitive matcher, so lines with any case will match. You can toggle between IgnoreCase, CaseSensitive, and RegExp matchers. The RegExp matcher allows you to use any valid regular expression to match lines

optimized

Works on Windows!

I have been told that peco even works on windows :)

Installation

Mac OS X / Homebrew

If you're on OS X and want to use homebrew, use my custom tap:

brew tap lestrrat/peco
brew install peco

optimized

go get

If you want to go the Go way (install in GOPATH/bin) and just want the command:

go get github.com/lestrrat/peco/cmd/peco

Usage

If you can read Japanese, here's one cool usage using ghq

Basically, you can define a simple function to easily move around your source code tree:

function peco-src () {
    local selected_dir=$(ghq list --full-path | peco --query "$LBUFFER")
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi    
    zle clear-screen
}         
zle -N peco-src

Or to easily navigate godoc for your local stuff:

function peco-godoc() { 
    local selected_dir=$(ghq list --full-path | peco --query "$LBUFFER")
    if [ -n "$selected_dir" ]; then
        BUFFER="godoc ${selected_dir} | less"
        zle accept-line 
    fi 
    zle clear-screen 
}
    
zle -N peco-godoc 

Command Line Options

--help

Display a help message

--query

Specifies the default query to be used upon startup. This is useful for scripts and functions where you can figure out before hand what the most likely query string is.

--rcfile

Pass peco a configuration file, which currently must be a JSON file. If unspecified it will try a series of files by default. See Configuration File for the actual locationes searched.

--no-ignore-case

By default peco starts in case insensitive mode. When this option is specified, peco will start in case sensitive mode. This can be toggled while peco is still in session.

Configuration File

peco by default consults a few locations for the config files.

  1. Location specified in --rcfile. If this doesn't exist, peco complains and exits
  2. $XDG_CONFIG_HOME/config.json
  3. $HOME/.config/peco/config.json
  4. for each directories listed in $XDG_CONFIG_DIRS, $DIR/peco/config.json
  5. If all else fails, $HOME/.peco/config.json

Below are configuration sections that you may specify in your config file:

Keymaps

Example:

{
    "Keymap": {
        "C-p": "peco.SelectPrevious",
        "C-n": "peco.SelectNext"
    }
}
Available keys
Name Notes
C-a ... C-z Control + whatever character
C-1 ... C-8 Control + 1..8
C-[
C-]
C-~
C-_
C-\\ Note that you need to escape the backslash
C-/
Esc
Tab
Insert
Delete
Home
End
Pgup
Pgdn
ArrowUp
ArrowDown
ArrowLeft
ArrowRight
Available actions
Name Notes
peco.ForwardChar Move caret forward 1 character
peco.BackwardChar Move caret backward 1 character
peco.ForwardWord Move caret forward 1 word
peco.BackwardWord Move caret backward 1 word
peco.BeginningOfLine Move caret to the beginning of line
peco.EndOfLine Move caret to the end of line
peco.EndOfFile Delete one character forward, otherwise exit from peco with failure status
peco.DeleteForwardChar Delete one character forward
peco.DeleteBackwardChar Delete one character backward
peco.DeleteForwardWord Delete one word forward
peco.DeleteBackwardWord Delete one word backward
peco.KillEndOfLine Delete the characters under the cursor until the end of the line
peco.DeleteAll Delete all entered characters
peco.SelectPreviousPage Jumps to previous page
peco.SelectNextPage Jumps to next page
peco.SelectPrevious Selects previous line
peco.SelectNext Selects next line
peco.RotateMatcher Rotate between matchers (by default, ignore-case/no-ignore-case)
peco.Finish Exits from peco, with success status
peco.Cancel Exits from peco, with failure status

Styles

For now, styles of following 3 items can be customized in config.json.

{
    "Style": {
        "Basic": ["on_default", "default"],
        "Selected": ["underline", "on_cyan", "black"],
        "Query": ["yellow", "bold"]
    }
}
Foreground Colors
  • "black" for termbox.ColorBlack
  • "red" for termbox.ColorRed
  • "green" for termbox.ColorGreen
  • "yellow" for termbox.ColorYellow
  • "blue" for termbox.ColorBlue
  • "magenta" for termbox.ColorMagenta
  • "cyan" for termbox.ColorCyan
  • "white" for termbox.ColorWhite
Background Colors
  • "on_black" for termbox.ColorBlack
  • "on_red" for termbox.ColorRed
  • "on_green" for termbox.ColorGreen
  • "on_yellow" for termbox.ColorYellow
  • "on_blue" for termbox.ColorBlue
  • "on_magenta" for termbox.ColorMagenta
  • "on_cyan" for termbox.ColorCyan
  • "on_white" for termbox.ColorWhite
Attributes
  • "bold" for termbox.AttrBold
  • "underline" for termbox.AttrUnderline
  • "blink" for termbox.AttrReverse

CustomMatcher

This is an experimental feature. Please note that some details of this specificaiton may change

By default peco comes with IgnoreCase, CaseSensitive, and Regexp matchers, but since v0.1.3, it is possible to create your own custom matcher.

The matcher will be executed via Command.Run() as an external process, and it will be passed the query values in the command line, and the original unaltered buffer is passed via os.Stdin. Your matcher must perform the matching, and print out to os.Stdout matched lines. Note that currently there is no way to specify where in the line the match occurred.

Once you have a matcher, you must specify how the matcher is spawned:

{
    "CustomMatcher": {
        "MyMatcher": [ "/path/to/my-matcher", "$QUERY" ]
    }
}

Elements in the CustomMatcher section are string keys to array of program arguments. The special token $QUERY will be replaced with the unaltered query as the user typed in (i.e. multiple-word queries will be passed as a single string). You may pass in any other arguments in this array.

You may specify as many matchers as you like.

Examples

Hacking

First, fork this repo, and get your clone locally.

  1. Make sure you have go 1.x, with GOPATH appropriately set
  2. Run go get github.com/jessevdk/go-flags
  3. Run go get github.com/mattn/go-runewidth
  4. Run go get github.com/nsf/termbox-go

Note that we have a Godeps file in source tree, for now it's just there for a peace of mind. If you already know about godep, when you may use that instead of steps 2~4

Then from the root of this repository run:

go build cmd/peco/peco.go

This will create a peco binary in the local directory.

TODO

Test it. In doing so, we may change the repo structure

Implement all(?) of the original percol options

AUTHORS

  • Daisuke Maki (lestrrat)
  • Shinya Ohyanagi
  • syohex
  • Takashi Kokubun
  • cho45
  • cubicdaiya
  • mattn
  • swdyh

Notes

Obviously, kudos to the original percol: https://github.com/mooz/percol Much code stolen from https://github.com/mattn/gof

Documentation

Index

Constants

View Source
const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	RegexpMatch        = "Regexp"
)

Variables

This section is empty.

Functions

func IsTty

func IsTty() bool

func LocateRcfile added in v0.1.3

func LocateRcfile() (string, error)

func TtyReady

func TtyReady() error

func TtyTerm

func TtyTerm()

Types

type CaseSensitiveMatcher added in v0.1.1

type CaseSensitiveMatcher struct {
	*RegexpMatcher
}

func NewCaseSensitiveMatcher added in v0.1.2

func NewCaseSensitiveMatcher() *CaseSensitiveMatcher

func (*CaseSensitiveMatcher) String added in v0.1.1

func (m *CaseSensitiveMatcher) String() string

type Config

type Config struct {
	Keymap        Keymap   `json:"Keymap"`
	Matcher       string   `json:"Matcher"`
	Style         StyleSet `json:"Style"`
	CustomMatcher map[string][]string
}

func NewConfig

func NewConfig() *Config

func (*Config) ReadFilename

func (c *Config) ReadFilename(filename string) error

type Ctx

type Ctx struct {
	Matchers       []Matcher
	CurrentMatcher int
	ExitStatus     int
	// contains filtered or unexported fields
}

Ctx contains all the important data. while you can easily access data in this struct from anwyehre, only do so via channels

func NewCtx

func NewCtx() *Ctx

func (*Ctx) AddMatcher added in v0.1.2

func (c *Ctx) AddMatcher(m Matcher)

func (*Ctx) AddWaitGroup

func (c *Ctx) AddWaitGroup(v int)

func (*Ctx) Buffer

func (c *Ctx) Buffer() []Match

func (*Ctx) DrawCh

func (c *Ctx) DrawCh() chan []Match

func (*Ctx) DrawMatches

func (c *Ctx) DrawMatches(m []Match)

func (*Ctx) ExecQuery

func (c *Ctx) ExecQuery(v string)

func (*Ctx) Finish

func (c *Ctx) Finish()

func (*Ctx) LoadCustomMatcher added in v0.1.3

func (c *Ctx) LoadCustomMatcher() bool

func (*Ctx) LoopCh

func (c *Ctx) LoopCh() chan struct{}

func (*Ctx) Matcher added in v0.1.1

func (c *Ctx) Matcher() Matcher

func (*Ctx) NewFilter

func (c *Ctx) NewFilter() *Filter

func (*Ctx) NewInput

func (c *Ctx) NewInput() *Input

func (*Ctx) NewView

func (c *Ctx) NewView() *View

func (*Ctx) PagingCh

func (c *Ctx) PagingCh() chan PagingRequest

func (*Ctx) QueryCh

func (c *Ctx) QueryCh() chan string

func (*Ctx) ReadBuffer

func (c *Ctx) ReadBuffer(input io.Reader) error

func (*Ctx) ReadConfig

func (c *Ctx) ReadConfig(file string) error

func (*Ctx) Refresh

func (c *Ctx) Refresh()

func (*Ctx) ReleaseWaitGroup

func (c *Ctx) ReleaseWaitGroup()

func (*Ctx) Result

func (c *Ctx) Result() []string

func (*Ctx) SetCurrentMatcher added in v0.1.2

func (c *Ctx) SetCurrentMatcher(n string) bool

func (*Ctx) SetQuery

func (c *Ctx) SetQuery(q []rune)

func (*Ctx) SignalHandlerLoop added in v0.1.2

func (c *Ctx) SignalHandlerLoop(sigCh chan os.Signal)

func (*Ctx) Terminate

func (c *Ctx) Terminate()

func (*Ctx) WaitDone

func (c *Ctx) WaitDone()

type CustomMatcher added in v0.1.3

type CustomMatcher struct {
	// contains filtered or unexported fields
}

func NewCustomMatcher added in v0.1.3

func NewCustomMatcher(name string, args []string) *CustomMatcher

func (*CustomMatcher) Match added in v0.1.3

func (m *CustomMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match

func (*CustomMatcher) String added in v0.1.3

func (m *CustomMatcher) String() string

type DidMatch added in v0.1.3

type DidMatch struct {
	// contains filtered or unexported fields
}

DidMatch contains the actual match, and the indices to the matches in the line

func (DidMatch) Indices added in v0.1.3

func (d DidMatch) Indices() [][]int

func (DidMatch) Line added in v0.1.3

func (d DidMatch) Line() string

type Filter

type Filter struct {
	*Ctx
	// contains filtered or unexported fields
}

func (*Filter) Loop

func (f *Filter) Loop()

func (*Filter) Work added in v0.1.3

func (f *Filter) Work(cancel chan struct{}, q string)

type IgnoreCaseMatcher added in v0.1.1

type IgnoreCaseMatcher struct {
	*RegexpMatcher
}

func NewIgnoreCaseMatcher added in v0.1.2

func NewIgnoreCaseMatcher() *IgnoreCaseMatcher

func (*IgnoreCaseMatcher) String added in v0.1.1

func (m *IgnoreCaseMatcher) String() string

type Input

type Input struct {
	*Ctx
}

func (*Input) Loop

func (i *Input) Loop()

type Keymap

type Keymap map[termbox.Key]KeymapHandler

func NewKeymap

func NewKeymap() Keymap

func (Keymap) Handler

func (km Keymap) Handler(k termbox.Key) KeymapHandler

func (Keymap) UnmarshalJSON

func (km Keymap) UnmarshalJSON(buf []byte) error

type KeymapHandler

type KeymapHandler func(*Input, termbox.Event)

type KeymapStringKey

type KeymapStringKey string

func (KeymapStringKey) ToKey

func (ksk KeymapStringKey) ToKey() (k termbox.Key, err error)

type Match

type Match interface {
	Line() string
	Indices() [][]int
}

Match defines the interface for matches. Note that to make drawing easier, we have a DidMatch and NoMatch types instead of using []Match and []string.

type Matcher added in v0.1.1

type Matcher interface {
	// Match takes in three parameters.
	//
	// The first chan is the channel where cancel requests are sent.
	// If you receive a request here, you should stop running your query.
	//
	// The second is the query. Do what you want with it
	//
	// The third is the buffer in which to match the query against.
	Match(chan struct{}, string, []Match) []Match
	String() string
}

Matcher interface defines the API for things that want to match against the buffer

type NoMatch added in v0.1.3

type NoMatch string

NoMatch is actually an alias to a regular string. It implements the Match interface, but just returns the underlying string with no matches

func (NoMatch) Indices added in v0.1.3

func (m NoMatch) Indices() [][]int

func (NoMatch) Line added in v0.1.3

func (m NoMatch) Line() string

type PagingRequest

type PagingRequest int
const (
	ToNextLine PagingRequest = iota
	ToNextPage
	ToPrevLine
	ToPrevPage
)

type RegexpMatcher added in v0.1.2

type RegexpMatcher struct {
	// contains filtered or unexported fields
}

func NewRegexpMatcher added in v0.1.2

func NewRegexpMatcher() *RegexpMatcher

func (*RegexpMatcher) Match added in v0.1.2

func (m *RegexpMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match

func (*RegexpMatcher) MatchAllRegexps added in v0.1.2

func (m *RegexpMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int

func (*RegexpMatcher) QueryToRegexps added in v0.1.2

func (m *RegexpMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error)

func (*RegexpMatcher) String added in v0.1.2

func (m *RegexpMatcher) String() string

type Selection added in v0.1.3

type Selection []int

func (*Selection) Add added in v0.1.3

func (s *Selection) Add(v int)

func (*Selection) Clear added in v0.1.3

func (s *Selection) Clear()

func (Selection) Has added in v0.1.3

func (s Selection) Has(v int) bool

func (Selection) Len added in v0.1.3

func (s Selection) Len() int

func (Selection) Less added in v0.1.3

func (s Selection) Less(i, j int) bool

func (*Selection) Remove added in v0.1.3

func (s *Selection) Remove(v int)

func (Selection) Swap added in v0.1.3

func (s Selection) Swap(i, j int)

type Style added in v0.1.2

type Style struct {
	// contains filtered or unexported fields
}

func (*Style) UnmarshalJSON added in v0.1.2

func (s *Style) UnmarshalJSON(buf []byte) error

type StyleSet added in v0.1.2

type StyleSet struct {
	Basic          Style `json:"Basic"`
	SavedSelection Style `json:"SavedSelection"`
	Selected       Style `json:"Selected"`
	Query          Style `json:"Query"`
}

func NewStyleSet added in v0.1.2

func NewStyleSet() StyleSet

type View

type View struct {
	*Ctx
}

func (*View) Loop

func (u *View) Loop()

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL