Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snippet directory bug fixes #323

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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