Add various list formats

pull/6/head
Mickaël Menu 3 years ago
parent e0aaa60955
commit 8ed6667743
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -152,7 +152,7 @@ func (d *NoteDAO) Find(callback func(note.Match) error, filters ...note.Filter)
ORDER BY title ASC
`)
} else {
filter := filters[0].(note.QueryFilter)
filter := filters[0].(note.MatchFilter)
return d.tx.Query(`
SELECT n.id, n.path, n.title, n.body, n.word_count,
n.created, n.modified, n.checksum,
@ -187,7 +187,6 @@ func (d *NoteDAO) Find(callback func(note.Match) error, filters ...note.Filter)
}
callback(note.Match{
ID: id,
Snippet: snippet,
Metadata: note.Metadata{
Path: path,

@ -1,14 +1,19 @@
package cmd
import (
"fmt"
"github.com/mickael-menu/zk/adapter/sqlite"
"github.com/mickael-menu/zk/core/note"
"github.com/mickael-menu/zk/core/zk"
"github.com/mickael-menu/zk/util/opt"
)
// List displays notes matching a set of criteria.
type List struct {
Query string `arg optional help:"Terms to search for in the notes" placeholder:"TERMS"`
Path string `arg optional placeholder:"PATH"`
Match string `help:"Terms to search for in the notes" placeholder:"TERMS"`
Format string `help:"Pretty prints the list using the given format" placeholder:"TEMPLATE"`
}
func (cmd *List) Run(container *Container) error {
@ -23,13 +28,29 @@ func (cmd *List) Run(container *Container) error {
}
return db.WithTransaction(func(tx sqlite.Transaction) error {
notes := sqlite.NewNoteDAO(tx, zk.Path, container.Logger)
notes := sqlite.NewNoteDAO(tx, container.Logger)
filters := make([]note.Filter, 0)
if cmd.Query != "" {
filters = append(filters, note.QueryFilter(cmd.Query))
if cmd.Match != "" {
filters = append(filters, note.MatchFilter(cmd.Match))
}
return note.List(notes, filters...)
return note.List(
note.ListOpts{
Format: opt.NewNotEmptyString(cmd.Format),
Filters: filters,
},
note.ListDeps{
BasePath: zk.Path,
Finder: notes,
Templates: container.TemplateLoader(zk.Config.Lang),
},
printNote,
)
})
}
func printNote(note string) error {
_, err := fmt.Println(note)
return err
}

@ -1,30 +1,160 @@
package note
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/fatih/color"
"github.com/mickael-menu/zk/core/templ"
"github.com/mickael-menu/zk/util/opt"
)
type QueryFilter string
// MatchFilter is a note filter used to match its content with FTS predicates.
type MatchFilter string
// Match holds information about a note matching the list filters.
type Match struct {
ID int
// Snippet is an excerpt of the note.
Snippet string
Metadata
}
// Finder retrieves notes matching the given Filter.
type Finder interface {
Find(callback func(Match) error, filters ...Filter) error
}
func List(finder Finder, filters ...Filter) error {
return finder.Find(func(note Match) error {
fmt.Printf("%v\n", strings.ReplaceAll(note.Snippet, "\\033", "\033"))
return nil
}, filters...)
// Unit represents a type of component of note, for example its path or its title.
type Unit int
const (
UnitPath Unit = iota + 1
UnitTitle
UnitBody
UnitSnippet
UnitMatch
UnitWordCount
UnitDate
UnitChecksum
)
type ListOpts struct {
Format opt.String
Filters []Filter
}
type ListDeps struct {
BasePath string
Finder Finder
Templates templ.Loader
}
// List finds notes matching given criteria and formats them according to user
// preference.
func List(opts ListOpts, deps ListDeps, callback func(formattedNote string) error) error {
templ := matchTemplate(opts.Format)
template, err := deps.Templates.Load(templ)
if err != nil {
return err
}
return deps.Finder.Find(func(note Match) error {
ft, err := format(note, deps.BasePath)
if err != nil {
return err
}
res, err := template.Render(ft)
if err != nil {
return err
}
return callback(res)
}, opts.Filters...)
}
var templates = map[string]string{
"path": `{{path}}`,
"oneline": `{{path}} {{title}} ({{date created "elapsed"}})`,
"short": `{{path}} {{title}} ({{date created "elapsed"}})
{{prepend " " snippet}}
`,
"medium": `{{path}} {{title}}
Created: {{date created "short"}}
{{prepend " " snippet}}
`,
"long": `{{path}} {{title}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
{{prepend " " snippet}}
`,
"full": `{{path}} {{title}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
{{prepend " " body}}
`,
}
func matchTemplate(format opt.String) string {
templ, ok := templates[format.OrDefault("short")]
if !ok {
templ = format.String()
// Replace raw \n and \t by actual newlines and tabs in user format.
templ = strings.ReplaceAll(templ, "\\n", "\n")
templ = strings.ReplaceAll(templ, "\\t", "\t")
}
return templ
}
func format(match Match, basePath string) (*matchRenderContext, error) {
color.NoColor = false // Otherwise the colors are not displayed in `less -r`.
path := color.New(color.FgCyan).SprintFunc()
title := color.New(color.FgYellow).SprintFunc()
term := color.New(color.FgRed).SprintFunc()
re := regexp.MustCompile(`<zk:match>(.*?)</zk:match>`)
wd, err := os.Getwd()
if err != nil {
return nil, err
}
pth, err := filepath.Rel(wd, filepath.Join(basePath, match.Path))
if err != nil {
return nil, err
}
return &matchRenderContext{
Path: path(pth),
Title: title(match.Title),
Body: match.Body,
WordCount: match.WordCount,
Snippet: strings.TrimSpace(re.ReplaceAllString(match.Snippet, term("$1"))),
Created: match.Created,
Modified: match.Modified,
}, err
}
type matchRenderContext struct {
Path string
Title string
Body string
WordCount int
Snippet string
Created time.Time
Modified time.Time
}
// Filter is a sealed interface implemented by Finder filter criteria.
type Filter interface{ sealed() }
func (f QueryFilter) sealed() {}
func (f MatchFilter) sealed() {}

@ -6,6 +6,7 @@ require (
github.com/alecthomas/kong v0.2.12
github.com/aymerick/raymond v2.0.2+incompatible
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/fatih/color v1.10.0
github.com/go-testfixtures/testfixtures/v3 v3.4.1
github.com/google/go-cmp v0.3.1
github.com/gosimple/slug v1.9.0
@ -16,6 +17,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.6
github.com/rvflash/elapsed v0.2.0
github.com/tebeka/strftime v0.1.5 // indirect
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad // indirect
gopkg.in/djherbis/times.v1 v1.2.0
gopkg.in/yaml.v2 v2.4.0 // indirect
)

@ -21,6 +21,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
@ -93,10 +95,14 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@ -168,7 +174,12 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad h1:MCsdmFSdEd4UEa5TKS5JztCRHK/WtvNei1edOj5RSRo=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

Loading…
Cancel
Save