Skip to content

Commit

Permalink
find command implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
bdazl committed Oct 27, 2024
1 parent 8fad25b commit b6a43e2
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 6 deletions.
112 changes: 112 additions & 0 deletions cmd/find.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright © 2024 Jacob Peyron <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd

import (
"fmt"
"regexp"
"strings"

"github.com/bdazl/note/db"
"github.com/spf13/cobra"
)

type finder struct {
*regexp.Regexp
Expression string
Insensitive bool
}

func finderFromArgs(args []string) (*finder, error) {
var (
expr = strings.Join(args, " ")
err error
regex *regexp.Regexp
)

if regexpArg && !posixArg {
regex, err = regexp.Compile(expr)
if err != nil {
return nil, fmt.Errorf("compile: %w", err)
}
} else if posixArg {
regex, err = regexp.CompilePOSIX(expr)
if err != nil {
return nil, fmt.Errorf("compile: %w", err)
}
}

return &finder{
Regexp: regex,
Expression: expr,
Insensitive: insensitiveArg,
}, nil
}

func (f *finder) Match(str string) bool {
if f.Regexp != nil {
return f.Regexp.MatchString(str)
} else if !f.Insensitive {
return strings.Contains(str, f.Expression)
} else {
// Make this more efficient
lower := strings.ToLower(str)
return strings.Contains(lower, f.Expression)
}
}

func noteFind(cmd *cobra.Command, args []string) {
finder, err := finderFromArgs(args)
if err != nil {
quitError("pattern", err)
}

style, color, err := styleColorOpts()
if err != nil {
quitError("args", err)
}

d := dbOpen()
defer d.Close()

notes := make(db.Notes, 0)
for iter := range d.IterateNotes(nil, allArg || trashArg, nil) {
if iter.Err != nil {
quitError("db iterate", iter.Err)
}

if !trashArg && iter.Note.Space == TrashSpace {
continue
}

if finder.Match(iter.Note.Content) {
notes = append(notes, iter.Note)
}
}

if idArg {
ids := notes.GetIDs()
printIds(ids, listArg)
} else {
pprintNotes(notes, style, color)
}
}
6 changes: 5 additions & 1 deletion cmd/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ func noteId(cmd *cobra.Command, args []string) {
quitError("db", err)
}

if listArg {
printIds(ids, listArg)
}

func printIds(ids []int, list bool) {
if list {
for _, id := range ids {
fmt.Println(id)
}
Expand Down
50 changes: 45 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,36 @@ will be shown.`,
spaceCmd = &cobra.Command{
Use: "space [id...]",
Aliases: []string{"spaces", "spc"},
Short: "Lists all or some spaces",
Short: "Lists available spaces",
Run: noteSpace,
Long: `Print available spaces occupied by notes.
If no ID's are given, all spaces will be printed.
If no ID's are given, all spaces not hidden will be printed.
By specifying ID's of notes, only the spaces occupied by those notes
will be shown.`,
}
findCmd = &cobra.Command{
Use: "find pattern [pattern...]",
Aliases: []string{"fd", "grep"},
Short: "Move note to another space",
Args: cobra.MinimumNArgs(1),
Run: noteFind,
Long: `Find the note containing the pattern
By default the pattern indicates a case sensitive string to be found.
Use --insensitive to change to case insensitive match. If --regexp is specified
the combined pattern is considered a regular expression.
If multiple patterns are input, they will be combined with spaces in between,
regardless if the pattern is a regexp or not.
When the --all option is set, hidden spaces will be searched excluding .trash.
If the --trash option is set, the .trash space is included.
If --trash is set, but not --all, .trash is the only hidden space searched.
The --posix flag restricts the regexp syntax to POSIX ERE (egrep) and the match
semantics is leftmost-longest. This option implies --regexp
See: https://pkg.go.dev/regexp#CompilePOSIX for details.`,
}
importCmd = &cobra.Command{
Use: "import file [file...]",
Expand Down Expand Up @@ -309,7 +332,7 @@ Files will only be imported once (per run), no checks for duplicate notes are ma
permanentArg bool

// List arguments
allArg bool // also used in spaces
allArg bool // used in a lot of places
sortByArg string
descendingArg bool
limitArg int
Expand All @@ -321,6 +344,13 @@ Files will only be imported once (per run), no checks for duplicate notes are ma
// Spaces arguments
listArg bool

// Find arguments
insensitiveArg bool
regexpArg bool
posixArg bool
trashArg bool
idArg bool

// Import/Export arguments
spacesArg []string
jsonArg bool
Expand Down Expand Up @@ -393,6 +423,16 @@ func init() {
getFlags := getCmd.Flags()
getFlags.AddFlagSet(printFlagSet)

findFlags := findCmd.Flags()
findFlags.AddFlagSet(printFlagSet)
findFlags.BoolVarP(&allArg, "all", "a", false, "show results from hidden spaces")
findFlags.BoolVarP(&trashArg, "trash", "t", false, "show results from trash")
findFlags.BoolVar(&idArg, "id", false, "print only IDs of matched notes")
findFlags.BoolVarP(&listArg, "list", "l", false, "separate IDs with newline")
findFlags.BoolVarP(&insensitiveArg, "insensitive", "i", false, "case insensitive match")
findFlags.BoolVarP(&regexpArg, "regexp", "r", false, "pattern is considered regular expressions")
findFlags.BoolVarP(&posixArg, "posix", "p", false, "pattern is posix egrep regular expressions (implies --regexp)")

idFlags := idCmd.Flags()
idFlags.BoolVarP(&listArg, "list", "l", false, "separate each ID with a newline")
idFlags.BoolVarP(&descendingArg, "descending", "d", false, "descending order")
Expand Down Expand Up @@ -430,8 +470,8 @@ func init() {
rootCmd.AddCommand(
initCmd,
versionCmd,
addCmd, removeCmd,
getCmd, listCmd, tableCmd, idCmd, spaceCmd,
addCmd, removeCmd, getCmd, findCmd,
listCmd, tableCmd, idCmd, spaceCmd,
editCmd, pinCmd, unpinCmd, moveCmd,
importCmd, exportCmd,
)
Expand Down
64 changes: 64 additions & 0 deletions db/iterate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright © 2024 Jacob Peyron <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package db

import "fmt"

type NoteIterator struct {
Note
Err error
}

func (d *DB) IterateNotes(spaces []string, all bool, sortOpts *SortOpts) <-chan NoteIterator {
ch := make(chan NoteIterator)

go func() {
defer close(ch)

page := 0
pageOpts := &PageOpts{
Limit: 10,
}

for {
notes, err := d.SelectNotes(spaces, all, sortOpts, pageOpts)
if err != nil {
fmt.Printf("Error: %v", err.Error())
ch <- NoteIterator{Err: err}
return
}

if len(notes) == 0 {
return
}

for _, note := range notes {
ch <- NoteIterator{Note: note}
}

page += 1
pageOpts.Offset = pageOpts.Limit * page
}
}()

return ch
}

0 comments on commit b6a43e2

Please sign in to comment.