Skip to content

Commit

Permalink
Refactor to use csv format
Browse files Browse the repository at this point in the history
  • Loading branch information
baopham committed Jun 26, 2017
1 parent 2fa9019 commit 615a007
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 102 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ USAGE:
snip [global options] command [command options] [arguments...]
VERSION:
1.0.2
2.0.0
COMMANDS:
add, a snip add -k="port" -c="lsof -i :{p}" -desc="List processes listening on a particular port"
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Action(fn func(c *cli.Context) error) func(c *cli.Context) error {

func main() {
app := cli.NewApp()
app.Version = "1.0.2"
app.Version = "2.0.0"
app.Usage = "Save snippets: commands, texts, emoji, etc."
app.EnableBashCompletion = true
app.Commands = []cli.Command{
Expand Down
145 changes: 61 additions & 84 deletions snippet/snippet.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package snippet

import (
"bufio"
"bytes"
"encoding/csv"
"fmt"
"io/ioutil"
"io"
"os"
"os/user"
"path"
"regexp"
"strings"

"github.com/baopham/snip/util"
"github.com/renstrom/fuzzysearch/fuzzy"
)

// EOL end of line
const EOL = "\n"

// SnippetSeparator between 2 snippets
const SnippetSeparator = ">>>>>>"

const FileMode = 0600

const (
Expand All @@ -42,6 +34,8 @@ type Snippet struct {
func (s *Snippet) Save(filePath string) error {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, FileMode)

defer util.Check(file.Close)

if err != nil {
return err
}
Expand All @@ -56,54 +50,62 @@ func (s *Snippet) Save(filePath string) error {
return SnippetAlreadyExistError{Keyword: existingSnippet.Keyword}
}

defer util.Check(file.Close)

info, err := file.Stat()
w := csv.NewWriter(file)

if err != nil {
if err := w.Write([]string{s.Keyword, s.Content, s.Description}); err != nil {
return err
}

content := s.String()
w.Flush()

if info.Size() > 0 {
content = EOL + SnippetSeparator + EOL + content
}

if _, err = file.WriteString(content); err != nil {
return err
}

return nil
return w.Error()
}

// Remove a saved snippet
func (s *Snippet) Remove(filePath string) error {
b, err := ioutil.ReadFile(filePath)
rows := make([][]string, 0)

file, err := os.Open(filePath)

if err != nil {
return err
}

content := string(b)
csvr := csv.NewReader(file)

re := regexp.MustCompile(fmt.Sprintf("(?m)(%s)*%s$%s*", SnippetSeparator+EOL, regexp.QuoteMeta(s.String()), EOL))
for {
row, err := csvr.Read()

newContent := re.ReplaceAllString(content, "")
if err == io.EOF {
break
}

if err != nil {
return err
}

lines := strings.Split(newContent, EOL)
keyword, content, description := row[0], row[1], row[2]

if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
if s.Keyword == keyword {
continue
}

rows = append(rows, []string{keyword, content, description})
}

if len(lines) > 0 && lines[0] == SnippetSeparator {
lines = lines[1:]
err = file.Close()

if err != nil {
return err
}

newContent = strings.Join(lines, EOL)
file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, FileMode)

defer util.Check(file.Close)

w := csv.NewWriter(file)

return ioutil.WriteFile(filePath, []byte(newContent), FileMode)
return w.WriteAll(rows)
}

// Get all saved snippets
Expand All @@ -112,7 +114,7 @@ func GetAll(filePath string) ([]*Snippet, error) {
}

// SearchExact exact search by keyword
func SearchExact(keyword string, filePath string) (*Snippet, error) {
func SearchExact(keyword, filePath string) (*Snippet, error) {
snippets, err := searchSnippets(keyword, filePath, SEARCH_EXACT)

if err != nil {
Expand All @@ -127,14 +129,10 @@ func SearchExact(keyword string, filePath string) (*Snippet, error) {
}

// Search fuzzy search by given search term
func Search(searchTerm string, filePath string) ([]*Snippet, error) {
func Search(searchTerm, filePath string) ([]*Snippet, error) {
return searchSnippets(searchTerm, filePath, SEARCH_FUZZY)
}

func (s *Snippet) String() string {
return fmt.Sprintf("%s|%s|%s", s.Keyword, s.Content, s.Description)
}

// Build snippet actual content using the given placeholders
func (s *Snippet) Build(placeholders map[string]string) string {
content := s.Content
Expand Down Expand Up @@ -167,7 +165,7 @@ func SnippetFile() (string, error) {
return "", err
}

filePath := path.Join(dir, "snippets")
filePath := path.Join(dir, "snippets.csv")

return filePath, err
}
Expand Down Expand Up @@ -195,47 +193,16 @@ func init() {
}
}

func getScanner(file *os.File) *bufio.Scanner {
const splitSeparator = EOL + SnippetSeparator + EOL

trimSeparator := func(data []byte) []byte {
return bytes.Trim(data, splitSeparator)
}

splitter := func(data []byte, atEOF bool) (advanced int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}

if i := strings.Index(string(data), splitSeparator); i >= 0 {
return i + 1, trimSeparator(data[0:i]), nil
}

if atEOF {
return len(data), trimSeparator(data), nil
}

return
}

scanner := bufio.NewScanner(file)

scanner.Split(splitter)

return scanner
}

func searchSnippets(searchTerm string, filePath string, exact SearchCode) ([]*Snippet, error) {
func searchSnippets(searchTerm, filePath string, exact SearchCode) ([]*Snippet, error) {
var snippets []*Snippet

file, err := os.Open(filePath)
defer util.Check(file.Close)

if err != nil {
return snippets, err
}

scanner := getScanner(file)

matcher := fuzzy.MatchFold

if exact == SEARCH_EXACT {
Expand All @@ -244,19 +211,29 @@ func searchSnippets(searchTerm string, filePath string, exact SearchCode) ([]*Sn
matcher = func(k, c string) bool { return true }
}

for line := 1; scanner.Scan(); line++ {
lineContent := scanner.Text()
csvr := csv.NewReader(file)

if !matcher(searchTerm, lineContent) {
continue
for {
row, err := csvr.Read()

if err == io.EOF {
return snippets, nil
}

if err != nil {
return snippets, err
}

record := strings.Split(lineContent, "|")
keyword, content, description := row[0], row[1], row[2]

if !matcher(searchTerm, keyword) && !matcher(searchTerm, content) && !matcher(searchTerm, description) {
continue
}

found := &Snippet{
Keyword: record[0],
Content: record[1],
Description: record[2],
Keyword: keyword,
Content: content,
Description: description,
}

snippets = append(snippets, found)
Expand All @@ -270,7 +247,7 @@ func searchSnippets(searchTerm string, filePath string, exact SearchCode) ([]*Sn
}

func exactMatcher(source string, target string) bool {
return regexp.MustCompile(fmt.Sprintf(`^%s\|`, source)).MatchString(target)
return strings.TrimSpace(source) == strings.TrimSpace(target)
}

func panicIfError(e error) {
Expand Down
Loading

0 comments on commit 615a007

Please sign in to comment.