Skip to content

Commit

Permalink
feat: multiple snippet directory support
Browse files Browse the repository at this point in the history
  • Loading branch information
helmecke committed Jan 10, 2022
1 parent e7a5e03 commit d0a2f76
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 19 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,37 @@ Run `pet configure`
id = "" # GitLab Snippets ID
visibility = "private" # public or internal or private
auto_sync = false # sync automatically when editing snippets
```

## Multi directory and multi file setup

Directories musst be specified as an array.
All `toml` files will be scraped and found snippets will be added.

Example1: single directory

```
$ pet configure
[General]
...
snippetdirs = ["/path/to/some/snippets/"]
...
```

Example2: multiple directories

```
$ pet configure
[General]
...
snippetdirs = ["/path/to/some/snippets/", "/more/snippets/"]
...
```
If `snippetfile` setting is omitted, new snippets will be added in a seperate file to the first directory. The generated filename is time based.

Snippet files in `snippetdirs` will not be added to Gist or GitLab. You've to do version control manually.


## Selector option
Example1: Change layout (bottom up)

Expand Down
24 changes: 24 additions & 0 deletions cmd/edit.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package cmd

import (
"fmt"
"io/ioutil"

"github.com/knqyf263/pet/config"
petSync "github.com/knqyf263/pet/sync"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"gopkg.in/alessio/shellescape.v1"
)

// editCmd represents the edit command
Expand All @@ -17,9 +20,26 @@ var editCmd = &cobra.Command{
}

func edit(cmd *cobra.Command, args []string) (err error) {
flag := config.Flag
editor := config.Conf.General.Editor
snippetFile := config.Conf.General.SnippetFile

var options []string
if flag.Query != "" {
options = append(options, fmt.Sprintf("--query %s", shellescape.Quote(flag.Query)))
}

if len(config.Conf.General.SnippetDirs) > 0 {
snippetFile, err = selectFile(options, flag.FilterTag)
if err != nil {
return err
}
}

if snippetFile == "" {
return errors.New("No sippet file seleted")
}

// file content before editing
before := fileContent(snippetFile)

Expand Down Expand Up @@ -50,4 +70,8 @@ func fileContent(fname string) string {

func init() {
RootCmd.AddCommand(editCmd)
editCmd.Flags().StringVarP(&config.Flag.Query, "query", "q", "",
`Initial value for query`)
editCmd.Flags().StringVarP(&config.Flag.FilterTag, "tag", "t", "",
`Filter tag`)
}
4 changes: 4 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func list(cmd *cobra.Command, args []string) error {
fmt.Fprintf(color.Output, "%s : %s\n",
color.GreenString(description), color.YellowString(command))
} else {
if config.Flag.Debug {
fmt.Fprintf(color.Output, "%12s %s\n",
color.RedString(" Filename:"), snippet.Filename)
}
fmt.Fprintf(color.Output, "%12s %s\n",
color.GreenString("Description:"), snippet.Description)
if strings.Contains(snippet.Command, "\n") {
Expand Down
9 changes: 7 additions & 2 deletions cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func scan(message string) (string, error) {
}

func new(cmd *cobra.Command, args []string) (err error) {
var filename string = ""
var command string
var description string
var tags []string
Expand Down Expand Up @@ -103,7 +104,12 @@ func new(cmd *cobra.Command, args []string) (err error) {
}
}

if config.Conf.General.SnippetFile != "" {
filename = config.Conf.General.SnippetFile
}

newSnippet := snippet.SnippetInfo{
Filename: filename,
Description: description,
Command: command,
Tag: tags,
Expand All @@ -113,9 +119,8 @@ func new(cmd *cobra.Command, args []string) (err error) {
return err
}

snippetFile := config.Conf.General.SnippetFile
if config.Conf.Gist.AutoSync {
return petSync.AutoSync(snippetFile)
return petSync.AutoSync(filename)
}

return nil
Expand Down
54 changes: 54 additions & 0 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,57 @@ func filter(options []string, tag string) (commands []string, err error) {
}
return commands, nil
}

func selectFile(options []string, tag string) (snippetFile string, err error) {
var snippets snippet.Snippets
if err := snippets.Load(); err != nil {
return snippetFile, fmt.Errorf("Load snippet failed: %v", err)
}

if 0 < len(tag) {
var filteredSnippets snippet.Snippets
for _, snippet := range snippets.Snippets {
for _, t := range snippet.Tag {
if tag == t {
filteredSnippets.Snippets = append(filteredSnippets.Snippets, snippet)
}
}
}
snippets = filteredSnippets
}

snippetTexts := map[string]snippet.SnippetInfo{}
var text string
for _, s := range snippets.Snippets {
command := s.Command
if strings.ContainsAny(command, "\n") {
command = strings.Replace(command, "\n", "\\n", -1)
}
t := fmt.Sprintf("[%s]: %s", s.Description, command)

tags := ""
for _, tag := range s.Tag {
tags += fmt.Sprintf(" #%s", tag)
}
t += tags

snippetTexts[t] = s
text += t + "\n"
}

var buf bytes.Buffer
selectCmd := fmt.Sprintf("%s %s",
config.Conf.General.SelectCmd, strings.Join(options, " "))
err = run(selectCmd, strings.NewReader(text), &buf)
if err != nil {
return snippetFile, nil
}

lines := strings.Split(strings.TrimSuffix(buf.String(), "\n"), "\n")

for _, line := range lines {
snippetInfo := snippetTexts[line]
snippetFile = fmt.Sprint(snippetInfo.Filename)
}
return snippetFile, nil
}
25 changes: 16 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os/exec"
"path/filepath"
"runtime"

"github.com/BurntSushi/toml"
"github.com/pkg/errors"
)
Expand All @@ -15,19 +16,20 @@ var Conf Config

// Config is a struct of config
type Config struct {
General GeneralConfig `toml:"General"`
Gist GistConfig `toml:"Gist"`
GitLab GitLabConfig `toml:"GitLab"`
General GeneralConfig `toml:"General"`
Gist GistConfig `toml:"Gist"`
GitLab GitLabConfig `toml:"GitLab"`
}

// GeneralConfig is a struct of general config
type GeneralConfig struct {
SnippetFile string `toml:"snippetfile"`
Editor string `toml:"editor"`
Column int `toml:"column"`
SelectCmd string `toml:"selectcmd"`
Backend string `toml:"backend"`
SortBy string `toml:"sortby"`
SnippetFile string `toml:"snippetfile"`
SnippetDirs []string `toml:"snippetdirs"`
Editor string `toml:"editor"`
Column int `toml:"column"`
SelectCmd string `toml:"selectcmd"`
Backend string `toml:"backend"`
SortBy string `toml:"sortby"`
}

// GistConfig is a struct of config for Gist
Expand Down Expand Up @@ -73,7 +75,12 @@ func (cfg *Config) Load(file string) error {
if err != nil {
return err
}
var snippetdirs []string
cfg.General.SnippetFile = expandPath(cfg.General.SnippetFile)
for _, dir := range cfg.General.SnippetDirs {
snippetdirs = append(snippetdirs, expandPath(dir)) // note the = instead of :=
}
cfg.General.SnippetDirs = snippetdirs
return nil
}

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ require (
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
)

require (
github.com/kennygrant/sanitize v1.2.4
gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
Expand Down
41 changes: 33 additions & 8 deletions snippet/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"fmt"
"os"
"sort"
"strings"

"github.com/BurntSushi/toml"
"github.com/kennygrant/sanitize"
"github.com/knqyf263/pet/config"
)

Expand All @@ -15,34 +17,57 @@ type Snippets struct {
}

type SnippetInfo struct {
Filename string `toml:"-"`
Description string `toml:"description"`
Command string `toml:"command"`
Tag []string `toml:"tag"`
Output string `toml:"output"`
Output string `toml:"output,omitempty"`
}

// Load reads toml file.
func (snippets *Snippets) Load() error {
snippetFile := config.Conf.General.SnippetFile
if _, err := os.Stat(snippetFile); os.IsNotExist(err) {
return nil
var files []string
if config.Conf.General.SnippetFile != "" {
files = append(files, config.Conf.General.SnippetFile)
}
if _, err := toml.DecodeFile(snippetFile, snippets); err != nil {
return fmt.Errorf("Failed to load snippet file. %v", err)
for _, dir := range config.Conf.General.SnippetDirs {
files = append(files, getFiles(dir)...)
}

for _, file := range files {
tmp := Snippets{}
if _, err := toml.DecodeFile(file, &tmp); err != nil {
return fmt.Errorf("Failed to load snippet file. %v", err)
}
for _, snippet := range tmp.Snippets {
snippet.Filename = file
snippets.Snippets = append(snippets.Snippets, snippet)
}
}

snippets.Order()
return nil
}

// Save saves the snippets to toml file.
func (snippets *Snippets) Save() error {
snippetFile := config.Conf.General.SnippetFile
var snippetFile string
var newSnippets Snippets
for _, snippet := range snippets.Snippets {
if snippet.Filename == "" {
snippetFile = config.Conf.General.SnippetDirs[0] + fmt.Sprintf("%s.toml", strings.ToLower(sanitize.BaseName(snippet.Description)))
newSnippets.Snippets = append(newSnippets.Snippets, snippet)
} else if snippet.Filename == config.Conf.General.SnippetFile {
snippetFile = config.Conf.General.SnippetFile
newSnippets.Snippets = append(newSnippets.Snippets, snippet)
}
}
f, err := os.Create(snippetFile)
defer f.Close()
if err != nil {
return fmt.Errorf("Failed to save snippet file. err: %s", err)
}
return toml.NewEncoder(f).Encode(snippets)
return toml.NewEncoder(f).Encode(newSnippets)
}

// ToString returns the contents of toml file.
Expand Down
28 changes: 28 additions & 0 deletions snippet/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package snippet

import (
"log"
"os"
"path/filepath"
"regexp"
)

func getFiles(path string) (fileList []string) {
tomlRegEx, err := regexp.Compile("^.+\\.(toml)$")
if err != nil {
log.Fatal(err)
}

err = filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
if err == nil && tomlRegEx.MatchString(f.Name()) {
fileList = append(fileList, path)
}
return nil
})

if err != nil {
panic(err)
}

return fileList
}

0 comments on commit d0a2f76

Please sign in to comment.