Skip to content

Commit

Permalink
Merge pull request #323 from knqyf263/snippet-directory-bug-fixes
Browse files Browse the repository at this point in the history
Snippet directory bug fixes
  • Loading branch information
RamiAwar authored Oct 24, 2024
2 parents 56b43be + 13e3074 commit 9bcebb7
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 37 deletions.
14 changes: 7 additions & 7 deletions cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func edit(cmd *cobra.Command, args []string) (err error) {
options = append(options, fmt.Sprintf("--query %s", shellescape.Quote(flag.Query)))
}

// If we have multiple snippet directories, we need to find the right
// snippet file to edit - so we need to prompt the user to select a snippet first
if len(config.Conf.General.SnippetDirs) > 0 {
snippetFile, err = selectFile(options, flag.FilterTag)
if err != nil {
Expand All @@ -41,21 +43,19 @@ func edit(cmd *cobra.Command, args []string) (err error) {
}

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

contentBefore := fileContent(snippetFile)
err = editFile(editor, snippetFile, 0)
if err != nil {
return
}
contentAfter := fileContent(snippetFile)

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

// return if same file content
if before == after {
// no need to try to sync if same file content
if contentBefore == contentAfter {
return nil
}

// sync snippet file
if config.Conf.Gist.AutoSync {
return petSync.AutoSync(snippetFile)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var listCmd = &cobra.Command{

func list(cmd *cobra.Command, args []string) error {
var snippets snippet.Snippets
if err := snippets.Load(); err != nil {
if err := snippets.Load(true); err != nil {
return err
}

Expand Down
21 changes: 14 additions & 7 deletions cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ func scanMultiLine(prompt string, secondMessage string, out io.Writer, in io.Rea
return "", errors.New("canceled")
}

// createAndEditSnippet creates and saves a given snippet, then opens the
// configured editor to edit the snippet file at startLine.
// createAndEditSnippet creates and saves a given snippet to the main snippet file
// then opens the configured editor to edit the snippet file at startLine.
func createAndEditSnippet(newSnippet snippet.SnippetInfo, snippets snippet.Snippets, startLine int) error {
snippets.Snippets = append(snippets.Snippets, newSnippet)
if err := snippets.Save(); err != nil {
Expand Down Expand Up @@ -179,14 +179,21 @@ func countSnippetLines() int {
return lineCount
}

// new creates a new snippet and saves it to the main snippet file
// then syncs the snippet file if configured to do so.
func new(cmd *cobra.Command, args []string) (err error) {
return _new(os.Stdin, os.Stdout, args)
}

func _new(in io.ReadCloser, out io.Writer, args []string) (err error) {
var filename string = ""
var command string
var description string
var tags []string

// Load snippets from the main file only
var snippets snippet.Snippets
if err := snippets.Load(); err != nil {
if err := snippets.Load(false); err != nil {
return err
}

Expand All @@ -200,7 +207,7 @@ func new(cmd *cobra.Command, args []string) (err error) {
command, err = scanMultiLine(
color.YellowString("Command> "),
color.YellowString(".......> "),
os.Stdout, os.Stdin,
out, in,
)
} else if config.Flag.UseEditor {
// Create and save empty snippet
Expand All @@ -213,20 +220,20 @@ func new(cmd *cobra.Command, args []string) (err error) {
return createAndEditSnippet(newSnippet, snippets, lineCount+3)

} else {
command, err = scan(color.HiYellowString("Command> "), os.Stdout, os.Stdin, false)
command, err = scan(color.HiYellowString("Command> "), out, in, false)
}
if err != nil {
return err
}
}
description, err = scan(color.HiGreenString("Description> "), os.Stdout, os.Stdin, false)
description, err = scan(color.HiGreenString("Description> "), out, in, false)
if err != nil {
return err
}

if config.Flag.Tag {
var t string
if t, err = scan(color.HiCyanString("Tag> "), os.Stdout, os.Stdin, true); err != nil {
if t, err = scan(color.HiCyanString("Tag> "), out, in, true); err != nil {
return err
}

Expand Down
124 changes: 124 additions & 0 deletions cmd/new_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ package cmd

import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"

"github.com/knqyf263/pet/config"
"github.com/knqyf263/pet/snippet"
"github.com/pelletier/go-toml"
)

// MockReadCloser is a mock implementation of io.ReadCloser
Expand Down Expand Up @@ -127,3 +133,121 @@ func TestScanMultiLine_ExitsOnTwoEmptyLines(t *testing.T) {
t.Errorf("Expected error %v, but got %v", expectedError, err)
}
}

func TestNewSnippetCreationWithSnippetDirectory(t *testing.T) {
// Setup temporary directory for config
tempDir := t.TempDir()
tempSnippetFile := filepath.Join(tempDir, "snippet.toml")
tempSnippetDir1 := filepath.Join(tempDir, "snippets1")
tempSnippetDir2 := filepath.Join(tempDir, "snippets2")

// Create snippet directories
if err := os.Mkdir(tempSnippetDir1, 0755); err != nil {
t.Fatalf("Failed to create temp snippet directory: %v", err)
}
if err := os.Mkdir(tempSnippetDir2, 0755); err != nil {
t.Fatalf("Failed to create temp snippet directory: %v", err)
}

// Create dummy snippets in the main snippet file
mainSnippets := snippet.Snippets{
Snippets: []snippet.SnippetInfo{
{Description: "main snippet 1", Command: "echo main1"},
{Description: "main snippet 2", Command: "echo main2"},
},
}
saveSnippetsToFile(t, tempSnippetFile, mainSnippets)

// Create dummy snippets in the snippet directories
dirSnippets1 := snippet.Snippets{
Snippets: []snippet.SnippetInfo{
{Description: "dir1 snippet 1", Command: "echo dir1-1"},
},
}
dirSnippets2 := snippet.Snippets{
Snippets: []snippet.SnippetInfo{
{Description: "dir2 snippet 1", Command: "echo dir2-1"},
},
}
saveSnippetsToFile(t, filepath.Join(tempSnippetDir1, "snippets1.toml"), dirSnippets1)
saveSnippetsToFile(t, filepath.Join(tempSnippetDir2, "snippets2.toml"), dirSnippets2)

// Mock configuration
config.Conf.General.SnippetFile = tempSnippetFile
config.Conf.General.SnippetDirs = []string{tempSnippetDir1, tempSnippetDir2}

// Simulate creating a new snippet
args := []string{"echo new command"}

// Create a buffer for output
var outputBuffer bytes.Buffer
// Create a mock ReadCloser for input
inputReader := &MockReadCloser{strings.NewReader("test\ntest")}

err := _new(inputReader, &outputBuffer, args)
if err != nil {
t.Fatalf("Failed to create new snippet: %v", err)
}

// Load the main snippet file and check:
// 1 - if the new snippet is added
// 2 - if the number of snippets is correct (to avoid bugs like overwriting with dir snippets)
var updatedMainSnippets snippet.Snippets
loadSnippetsFromFile(t, tempSnippetFile, &updatedMainSnippets)

if len(updatedMainSnippets.Snippets) != 3 {
t.Fatalf("Expected 3 snippets in main snippet file, got %d", len(updatedMainSnippets.Snippets))
}

newSnippet := updatedMainSnippets.Snippets[2]
if newSnippet.Command != "echo new command" {
t.Errorf("Expected new command to be 'echo new command', got '%s'", newSnippet.Command)
}

// Ensure the snippet files in the directories remain unchanged
var unchangedDirSnippets1, unchangedDirSnippets2 snippet.Snippets
loadSnippetsFromFile(t, filepath.Join(tempSnippetDir1, "snippets1.toml"), &unchangedDirSnippets1)
loadSnippetsFromFile(t, filepath.Join(tempSnippetDir2, "snippets2.toml"), &unchangedDirSnippets2)

if !compareSnippets(dirSnippets1, unchangedDirSnippets1) {
t.Errorf("Snippets in directory 1 have changed")
}
if !compareSnippets(dirSnippets2, unchangedDirSnippets2) {
t.Errorf("Snippets in directory 2 have changed")
}
}

func saveSnippetsToFile(t *testing.T, filename string, snippets snippet.Snippets) {
f, err := os.Create(filename)
if err != nil {
t.Fatalf("Failed to create snippet file: %v", err)
}
defer f.Close()

if err := toml.NewEncoder(f).Encode(snippets); err != nil {
t.Fatalf("Failed to encode snippets to file: %v", err)
}
}

func loadSnippetsFromFile(t *testing.T, filename string, snippets *snippet.Snippets) {
f, err := os.ReadFile(filename)
if err != nil {
t.Fatalf("Failed to read snippet file: %v", err)
}

if err := toml.Unmarshal(f, snippets); err != nil {
t.Fatalf("Failed to unmarshal snippets from file: %v", err)
}
}

func compareSnippets(a, b snippet.Snippets) bool {
if len(a.Snippets) != len(b.Snippets) {
return false
}
for i := range a.Snippets {
if a.Snippets[i].Description != b.Snippets[i].Description || a.Snippets[i].Command != b.Snippets[i].Command {
return false
}
}
return true
}
6 changes: 2 additions & 4 deletions cmd/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import (

"github.com/knqyf263/pet/config"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
"gopkg.in/alessio/shellescape.v1"
)

var delimiter string

// searchCmd represents the search command
var searchCmd = &cobra.Command{
Use: "search",
Expand All @@ -33,7 +31,7 @@ func search(cmd *cobra.Command, args []string) (err error) {
}

fmt.Print(strings.Join(commands, flag.Delimiter))
if terminal.IsTerminal(1) {
if term.IsTerminal(1) {
fmt.Print("\n")
}
return nil
Expand Down
13 changes: 10 additions & 3 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ func editFile(command, file string, startingLine int) error {

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

// Filter the snippets by specified tag if any
if 0 < len(tag) {
var filteredSnippets snippet.Snippets
for _, snippet := range snippets.Snippets {
Expand Down Expand Up @@ -103,12 +104,16 @@ func filter(options []string, tag string) (commands []string, err error) {
return commands, nil
}

// selectFile returns a snippet file path from the list of snippets
// options are simply the list of arguments to pass to the select command (ex. --query for fzf)
// tag is used to filter the list of snippets by the tag field in the snippet
func selectFile(options []string, tag string) (snippetFile string, err error) {
var snippets snippet.Snippets
if err := snippets.Load(); err != nil {
if err := snippets.Load(true); err != nil {
return snippetFile, fmt.Errorf("load snippet failed: %v", err)
}

// Filter the snippets by specified tag if any
if 0 < len(tag) {
var filteredSnippets snippet.Snippets
for _, snippet := range snippets.Snippets {
Expand All @@ -121,6 +126,7 @@ func selectFile(options []string, tag string) (snippetFile string, err error) {
snippets = filteredSnippets
}

// Create a map of (desc, command, tags) string to SnippetInfo
snippetTexts := map[string]snippet.SnippetInfo{}
var text string
for _, s := range snippets.Snippets {
Expand All @@ -140,6 +146,7 @@ func selectFile(options []string, tag string) (snippetFile string, err error) {
text += t + "\n"
}

// Build the select command with options and run it
var buf bytes.Buffer
selectCmd := fmt.Sprintf("%s %s",
config.Conf.General.SelectCmd, strings.Join(options, " "))
Expand All @@ -148,8 +155,8 @@ func selectFile(options []string, tag string) (snippetFile string, err error) {
return snippetFile, nil
}

// Parse the selected line and return the corresponding snippet file
lines := strings.Split(strings.TrimSuffix(buf.String(), "\n"), "\n")

for _, line := range lines {
snippetInfo := snippetTexts[line]
snippetFile = fmt.Sprint(snippetInfo.Filename)
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ require (
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.1 // indirect
github.com/xanzy/go-gitlab v0.50.3
//github.com/xanzy/go-gitlab v0.10.5
golang.org/x/crypto v0.17.0
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
)
Expand All @@ -33,6 +31,7 @@ require (
github.com/awesome-gocui/gocui v1.1.0
github.com/go-test/deep v1.1.0
github.com/pelletier/go-toml v1.9.5
golang.org/x/term v0.15.0
)

require (
Expand All @@ -47,7 +46,6 @@ require (
github.com/rivo/uniseg v0.1.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/appengine v1.3.0 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ github.com/xanzy/go-gitlab v0.50.3 h1:M7ncgNhCN4jaFNyXxarJhCLa9Qi6fdmCxFFhMTQPZi
github.com/xanzy/go-gitlab v0.50.3/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
Expand Down
Loading

0 comments on commit 9bcebb7

Please sign in to comment.