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

Add zk config alias, resolves feature request in issue #253. #484

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 deletions docs/config/config-alias.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ conf = '$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml"'

Use this alias to send a list of space-separated file paths matching the given
[filtering criteria](../notes/note-filtering.md) to another program. See
[send notes for processing by other programs](../tips/external-processing.md) for more
details.
[send notes for processing by other programs](../tips/external-processing.md)
for more details.

```toml
paths = "zk list --quiet --format \"'{{path}}'\" --delimiter ' ' $@"
Expand Down Expand Up @@ -235,3 +235,8 @@ cp = 'mkdir -p "$1" && zk list --quiet --format path --delimiter0 ${@:2} | xargs
```

Usage: `zk cp output/ --created-after 'last two weeks'`

## Listing aliases

You can list all the aliases found in your configuration file using
`zk config --list aliases`.
13 changes: 9 additions & 4 deletions docs/config/config-extra.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
[creating new notes](../notes/note-creation.md), for example:

- expanding custom metadata (author, subject, etc.)
- modifying a [template](../notes/template.md)'s output dynamically depending on the
value of an extra variable
- modifying a [template](../notes/template.md)'s output dynamically depending on
the value of an extra variable

## Static extra variables

Expand Down Expand Up @@ -36,8 +36,8 @@ $ zk new --extra show-header=1,author=Thomas
## Using extra variables in templates

After declaring extra variables, you can expand them inside the
[template used when creating new notes](../notes/template-creation.md), using the usual
[Handlebars syntax](../notes/template.md).
[template used when creating new notes](../notes/template-creation.md), using
the usual [Handlebars syntax](../notes/template.md).

```markdown
# {{title}}
Expand All @@ -46,3 +46,8 @@ Written by {{extra.author}}.

{{#if extra.show-header}} Behold, the mighty dynamic header! {{/if}}
```

## Listing extras

You can list all the extras found in your configuration file using
`zk config --list extras`.
5 changes: 5 additions & 0 deletions docs/config/config-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@ $ zk list --sort created journal
# With the filter
$ zk list journal
```

## Listing filters

You can list all the filters found in your configuration file using
`zk config --list filters`.
145 changes: 145 additions & 0 deletions internal/cli/cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package cmd

import (
"fmt"
"io"
"os"
"sort"

"github.com/zk-org/zk/internal/cli"
"github.com/zk-org/zk/internal/util/errors"
"github.com/zk-org/zk/internal/util/strings"
)

// AliasList lists all the aliases.
type Config struct {
List string `short:l placeholder:OBJECT help:"List configuration objects. Listable ojects are: aliases, filters and extras."`
Format string `group:format short:f placeholder:TEMPLATE help:"Pretty print the list using a custom template or one of the predefined formats: short, full, json, jsonl."`
Header string `group:format help:"Arbitrary text printed at the start of the list."`
Footer string `group:format default:\n help:"Arbitrary text printed at the end of the list."`
Delimiter string "group:format short:d default:\n help:\"Print tags delimited by the given separator.\""
Delimiter0 bool "group:format short:0 name:delimiter0 help:\"Print tags delimited by ASCII NUL characters. This is useful when used in conjunction with `xargs -0`.\""
NoPager bool `group:format short:P help:"Do not pipe output into a pager."`
Quiet bool `group:format short:q help:"Do not print the total number of tags found."`
}

func (cmd *Config) Run(container *cli.Container) error {
cmd.Header = strings.ExpandWhitespaceLiterals(cmd.Header)
cmd.Footer = strings.ExpandWhitespaceLiterals(cmd.Footer)
cmd.Delimiter = strings.ExpandWhitespaceLiterals(cmd.Delimiter)

if cmd.Delimiter0 {
if cmd.Delimiter != "\n" {
return errors.New("--delimiter and --delimiter0 can't be used together")
}
if cmd.Header != "" {
return errors.New("--footer and --delimiter0 can't be used together")
}
if cmd.Footer != "\n" {
return errors.New("--footer and --delimiter0 can't be used together")
}

cmd.Delimiter = "\x00"
cmd.Footer = "\x00"
}

if cmd.Format == "json" || cmd.Format == "jsonl" {
if cmd.Header != "" {
return errors.New("--header can't be used with JSON format")
}
if cmd.Footer != "\n" {
return errors.New("--footer can't be used with JSON format")
}
if cmd.Delimiter != "\n" {
return errors.New("--delimiter can't be used with JSON format")
}

switch cmd.Format {
case "json":
cmd.Delimiter = ","
cmd.Header = "["
cmd.Footer = "]\n"

case "jsonl":
// > The last character in the file may be a line separator, and it
// > will be treated the same as if there was no line separator
// > present.
// > https://jsonlines.org/
cmd.Footer = "\n"
}
}

var objects = make(map[string]string)

switch cmd.List {
case "filters":
objects = container.Config.Filters
case "aliases":
objects = container.Config.Aliases
case "extras":
objects = container.Config.Extra
default:
fmt.Print("Listable config objects are: filters, aliases and extras.\n")
os.Exit(1)
}

count := len(objects)
keys := make([]string, count)
i := 0
for k := range objects {
keys[i] = k
i++
}
sort.Strings(keys)

format := cmd.mapTemplate()

var err = container.Paginate(cmd.NoPager, func(out io.Writer) error {
if cmd.Header != "" {
fmt.Fprint(out, cmd.Header)
}
for i, o := range keys {

if i > 0 {
fmt.Fprint(out, cmd.Delimiter)
}
if cmd.Format == "" || cmd.Format == "short" {
fmt.Fprintf(out, format, o)
} else {
fmt.Fprintf(out, format, o, objects[o])
}

i += 1
}
if cmd.Footer != "" {
fmt.Fprint(out, cmd.Footer)
}
return nil
})

if err == nil && !cmd.Quiet {
fmt.Fprintf(os.Stderr, "\nFound %d %s\n", count, cmd.List)
}
return err
}

func (cmd *Config) mapTemplate() string {
format := cmd.Format
if format == "" {
format = "short"
}

templ, ok := defaultMapFormats[format]
if !ok {
templ = strings.ExpandWhitespaceLiterals(format)
}

return templ
}

var defaultMapFormats = map[string]string{
"json": `{"%s":"%s"}`,
"jsonl": `{"%s":"%s"}`,
"short": `%s`,
"full": `%12s %s`,
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var Build = "dev"
var root struct {
Init cmd.Init `cmd group:"zk" help:"Create a new notebook in the given directory."`
Index cmd.Index `cmd group:"zk" help:"Index the notes to be searchable."`
Config cmd.Config `cmd group:"zk" help:"List configuration parameters."`

New cmd.New `cmd group:"notes" help:"Create a new note in the given notebook directory."`
List cmd.List `cmd group:"notes" help:"List notes matching the given criteria."`
Expand Down
88 changes: 88 additions & 0 deletions tests/cmd-config-list.tesh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
$ cd config-list

# Print help for `zk config`.
$ zk config --help
>Usage: zk config
>
>List configuration parameters.
>
>Flags:
> -h, --help Show context-sensitive help.
> --notebook-dir=PATH Turn off notebook auto-discovery and set manually
> the notebook where commands are run.
> -W, --working-dir=PATH Run as if zk was started in <PATH> instead of the
> current working directory.
> --no-input Never prompt or ask for confirmation.
>
> -l, --list=OBJECT list configuration objects. Possible ojects are
> aliases, filters or extras.
>
>Formatting
> -f, --format=TEMPLATE Pretty print the list using a custom template or one
> of the predefined formats: short, full, json, jsonl.
> --header=STRING Arbitrary text printed at the start of the list.
> --footer="\\n" Arbitrary text printed at the end of the list.
> -d, --delimiter="\n" Print tags delimited by the given separator.
> -0, --delimiter0 Print tags delimited by ASCII NUL characters. This is
> useful when used in conjunction with `xargs -0`.
> -P, --no-pager Do not pipe output into a pager.
> -q, --quiet Do not print the total number of tags found.

# Print short aliases list.
$ zk config --list aliases
>conf
>editlast
>hist
>list
>ls
>lucky
>path
>recent
2>
2>Found 8 aliases

# Print full aliases list.
$ zk config --list aliases --format=full
> conf $EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml"
> editlast zk edit --limit 1 --sort modified- $@
> hist zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --
> list zk list --quiet $@
> ls zk list $@
> lucky zk list --quiet --format full --sort random --limit 1
> path zk list --quiet --format \{{path}} --delimiter , $@
> recent zk edit --sort created- --created-after 'last two weeks' --interactive
2>
2>Found 8 aliases

# Print aliases as json.
$ zk config --list aliases --format=json
>[{"conf":"$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml""},{"editlast":"zk edit --limit 1 --sort modified- $@"},{"hist":"zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --"},{"list":"zk list --quiet $@"},{"ls":"zk list $@"},{"lucky":"zk list --quiet --format full --sort random --limit 1"},{"path":"zk list --quiet --format \{{path}} --delimiter , $@"},{"recent":"zk edit --sort created- --created-after 'last two weeks' --interactive"}]
2>
2>Found 8 aliases

# Print aliases as jsonl.
$ zk config --list aliases --format=jsonl
>{"conf":"$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml""}
>{"editlast":"zk edit --limit 1 --sort modified- $@"}
>{"hist":"zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --"}
>{"list":"zk list --quiet $@"}
>{"ls":"zk list $@"}
>{"lucky":"zk list --quiet --format full --sort random --limit 1"}
>{"path":"zk list --quiet --format \{{path}} --delimiter , $@"}
>{"recent":"zk edit --sort created- --created-after 'last two weeks' --interactive"}
2>
2>Found 8 aliases

# Print full filters list.
$ zk config --list filters --format=full
> journal --sort created journal
2>
2>Found 1 filters

# Print full extras list.
$ zk config --list extras --format=full
> author Mickaël
> visibility public
2>
2>Found 2 extras

38 changes: 38 additions & 0 deletions tests/fixtures/config-list/.zk/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[alias]
# Here are a few aliases to get you started.

# Shortcut to a command.
ls = "zk list $@"

# Default flags for an existing command.
list = "zk list --quiet $@"

# Edit the last modified note.
editlast = "zk edit --limit 1 --sort modified- $@"

# Edit the notes selected interactively among the notes created the last two weeks.
# This alias doesn't take any argument, so we don't use $@.
recent = "zk edit --sort created- --created-after 'last two weeks' --interactive"

# Print paths separated with colons for the notes found with the given
# arguments. This can be useful to expand a complex search query into a flag
# taking only paths. For example:
# zk list --link-to "`zk path -m potatoe`"
path = "zk list --quiet --format {{path}} --delimiter , $@"

# Show a random note.
lucky = "zk list --quiet --format full --sort random --limit 1"

# Returns the Git history for the notes found with the given arguments.
# Note the use of a pipe and the location of $@.
hist = "zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --"

# Edit this configuration file.
conf = '$EDITOR "$ZK_NOTEBOOK_DIR/.zk/config.toml"'

[filter]
journal = "--sort created journal"

[extra]
visibility = "public"
author = "Mickaël"