--- /dev/null
+pe
+*.txt
+demo.gif
--- /dev/null
+# PathExtractor (pe)
+
+
+
+PathExtractor is a command line tool that extracts a list of files/paths from stdin.
+
+Advantages over [fpp](https://github.com/facebook/PathPicker):
+
+ * It does only one thing : more unixy
+ * You can use it with any fuzzy finder, such as [fzf](https://github.com/junegunn/fzf),[peco](https://github.com/peco/peco),[percol](https://github.com/mooz/percol),[pick](https://github.com/thoughtbot/pick),[selecta](https://github.com/garybernhardt/selecta/)
+ * It doesn't wait for stdin to be finished to output the paths
+ * It is faster
+ * It is much smaller (easily understandable)
+ * You can also use it without a fuzzy finder for programmatic usage
+
+For example, you could write:
+
+ git status | pe
+
+to get a list of the files that were added/changed, without all the formating
+
+One of the most common usage is to create an alias that will automatically run :
+
+ `pe` + a command line fuzzy finder such as fzf + an action such as opening that file in your favorite editor.
+
+For example, using `zsh` , I have as an alias:
+
+ alias -g P='| pe | fzf | read filename; [ ! -z $filename ] && vim $filename'
+
+With `bash`:
+
+ bind '"PP": "| pe | fzf | read filename; [ ! -z $filename ] && vim $filename"'
+
+So that If I run
+
+ `git status P`
+
+or
+
+ `git status PP`
+
+to quickly open one of the changed files in vim
+
+Other usage ideas:
+
+With zsh:
+
+ # Copy selected path to clipboard
+ alias -g C='| pe | fzf | read filename; [ ! -z $filename ] && echo -n $filename | xclip -selection c'
+
+With bash:
+
+ bind '"CC": "| pe | fzf | read filename; [ ! -z $filename ] && echo -n $filename | xclip -selection c"'
+
+# Installation
+
+```
+git clone # in your go path
+
+go test
+
+go build
+
+go install
+```
--- /dev/null
+package main
+
+import "regexp"
+
+
+func pathExtractor(input string) [][][]byte {
+ surroundRegex := "[^][ \\t:'\"]"
+ r := regexp.MustCompile("("+surroundRegex+"*[\\./]"+surroundRegex+"*)")
+ temp := [][][]byte{}
+ temp = r.FindAllSubmatch([]byte(input),-1)
+ return temp
+}
+
+func getAllMatches(input string) []string {
+ matches := [][][]byte{}
+ result := []string{}
+ s := string("")
+ matches = pathExtractor(input)
+ for _,match := range matches {
+ s = string(match[1])
+ if isDate(s) || isVersion(s) || isGitRange(s) || isGitInstruction(s) || containsInvalidString(s) || len(s)<=2 {
+ continue
+ }
+ if isGitPath(s) {
+ s = replaceGitPath(s)
+ }
+ result = append(result,s)
+ }
+ return result
+}
--- /dev/null
+package main
+
+import "os"
+import "bufio"
+import "fmt"
+
+func main() {
+ stdin := os.Stdin
+ if scanner := bufio.NewScanner(stdin); scanner != nil {
+ for scanner.Scan() {
+ matches := getAllMatches(scanner.Text())
+ for _,match := range matches {
+ fmt.Println(match)
+ }
+ }
+ }
+}
+
--- /dev/null
+package main
+
+import "testing"
+
+func TestGitIgnore(t *testing.T) {
+ output:=getAllMatches("?? alt/generateStore.php")
+ if output[0] != "alt/generateStore.php" {
+ t.Errorf("Doesnt match files", output[0])
+ }
+
+ output=getAllMatches("hello .gitignore")
+ if output[0] != ".gitignore" {
+ t.Errorf("Doesnt match hidden files", output[0])
+ }
+
+ output=getAllMatches(" mail@mail.com ")
+ if len(output) != 0 {
+ t.Errorf("Matches email adresses", output[0])
+ }
+
+ output=getAllMatches("v1.2")
+ if len(output) != 0 {
+ t.Errorf("Matches version number", output[0])
+ }
+
+ output=getAllMatches("obj.slice()")
+ if len(output) != 0 {
+ t.Errorf("Matches function call", output[0])
+ }
+
+ output=getAllMatches("~/www")
+ if len(output) == 0 || output[0] != "~/www" {
+ t.Errorf("Doesnt match home", output[0])
+ }
+
+ output=getAllMatches("origin/master")
+ if len(output) != 0 {
+ t.Errorf("Matches remote name", output[0])
+ }
+
+ output=getAllMatches("john doe (dead on 28/04/2014)")
+ if len(output) != 0 {
+ t.Errorf("Matches date", output[0])
+ }
+
+ output=getAllMatches("john doe ,dead on 28/04/2014")
+ if len(output) != 0 {
+ t.Errorf("Matches date", output[0])
+ }
+
+ output=getAllMatches(".gitignore , ~/www")
+ if len(output) != 2 {
+ t.Errorf("Doesnt match multi", output[0])
+ }
+
+ output=getAllMatches("var/")
+ if len(output) != 1 {
+ t.Errorf("Doesnt match dir", output[0])
+ }
+
+ output=getAllMatches("//")
+ if len(output) != 0 {
+ t.Errorf("Comment matches", output[0])
+ }
+}
--- /dev/null
+package main
+
+import "strings"
+import "regexp"
+
+func isGitRange (input string) bool {
+ r := regexp.MustCompile("[0-9a-f]{3,}\\.\\.[0-9a-f]{3,}")
+ return r.Match([]byte(input))
+}
+
+func isGitPath (input string) bool {
+ r := regexp.MustCompile("^[ab]/")
+ return r.Match([]byte(input))
+}
+
+func isDate (input string) bool {
+ r := regexp.MustCompile("^[0-9]+/[0-9]+/[0-9]+")
+ return r.Match([]byte(input))
+}
+
+func isGitInstruction (input string) bool {
+ r := regexp.MustCompile("\\.{3,}")
+ return r.Match([]byte(input))
+}
+
+func replaceGitPath (input string) string {
+ r := regexp.MustCompile("^[ab]/(.*)")
+ temp := [][]byte{}
+ temp = r.FindSubmatch([]byte(input))
+ return string(temp[1])
+}
+
+func isVersion (input string) bool {
+ r := regexp.MustCompile("[0-9x]\\.[0-9x]{1,2}(\\.[0-9x]{1,3})?")
+ return r.Match([]byte(input))
+}
+
+func containsInvalidString (input string) bool {
+ invalidStrings := []string{"(",")","@","origin/","{","}","<",">","$","*"}
+ for _,s := range invalidStrings {
+ if strings.Contains(input,s) {
+ return true
+ }
+ }
+ return false
+}