Add `tesh` test suite (#147)

pull/153/head
Mickaël Menu 2 years ago committed by GitHub
parent 6ccbbe8613
commit eb095e5fc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

3
.gitignore vendored

@ -14,4 +14,5 @@
# Dependency directories (remove the comment below to include it)
# vendor/
.zk
notebook.db
zk

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Added
* New `--date` flag for `zk new` to set the current date manually.
* [#144](https://github.com/mickael-menu/zk/issues/144) LSP auto-completion of YAML frontmatter tags.
### Fixed

@ -10,6 +10,14 @@ install:
test:
$(call go,test,./...)
# Run end-to-end tests.
tesh: build
@PATH=$(shell pwd):$(PATH) tesh tests tests
# Update end-to-end tests.
tesh-update: build
PATH=$(shell pwd):$(PATH) tesh -u tests tests
# Produce a release bundle for all platforms.
dist: dist-macos dist-linux
rm -f zk

@ -79,8 +79,12 @@ func executeCommandNew(notebook *core.Notebook, documents *documentStore, contex
return nil, err
}
currentDir := filepath.Dir(doc.Path)
linkFormatterContext, err := core.NewLinkFormatterContext(note.AsMinimalNote(), notebook.Path, currentDir)
path := core.NotebookPath{
Path: note.Path,
BasePath: notebook.Path,
WorkingDir: filepath.Dir(doc.Path),
}
linkFormatterContext, err := core.NewLinkFormatterContext(path, note.Title, note.Metadata)
if err != nil {
return nil, err
}

@ -33,14 +33,13 @@ type Server struct {
// ServerOpts holds the options to create a new Server.
type ServerOpts struct {
Name string
Version string
LogFile opt.String
Logger *util.ProxyLogger
Notebooks *core.NotebookStore
NoteContentParser core.NoteContentParser
TemplateLoader core.TemplateLoader
FS core.FileStorage
Name string
Version string
LogFile opt.String
Logger *util.ProxyLogger
Notebooks *core.NotebookStore
TemplateLoader core.TemplateLoader
FS core.FileStorage
}
// NewServer creates a new Server instance.
@ -61,13 +60,12 @@ func NewServer(opts ServerOpts) *Server {
}
server := &Server{
server: glspServer,
notebooks: opts.Notebooks,
documents: newDocumentStore(fs, opts.Logger),
noteContentParser: opts.NoteContentParser,
templateLoader: opts.TemplateLoader,
fs: fs,
logger: opts.Logger,
server: glspServer,
notebooks: opts.Notebooks,
documents: newDocumentStore(fs, opts.Logger),
templateLoader: opts.TemplateLoader,
fs: fs,
logger: opts.Logger,
}
var clientCapabilities protocol.ClientCapabilities
@ -636,7 +634,7 @@ func (s *Server) refreshDiagnosticsOfDocument(doc *document, notify glsp.NotifyF
// buildInvokedCompletionList builds the completion item response for a
// completion started automatically when typing an identifier, or manually.
func (s *Server) buildInvokedCompletionList(notebook *core.Notebook, doc *document, position protocol.Position) ([]protocol.CompletionItem, error) {
if !doc.IsTagPosition(position, s.noteContentParser) {
if !doc.IsTagPosition(position, notebook.Parser) {
return nil, nil
}
return s.buildTagCompletionList(notebook, doc.WordAt(position))
@ -810,8 +808,12 @@ func (s *Server) newCompletionItem(notebook *core.Notebook, note core.MinimalNot
}
func (s *Server) newTextEditForLink(notebook *core.Notebook, note core.MinimalNote, doc *document, pos protocol.Position, linkFormatter core.LinkFormatter) (interface{}, error) {
currentDir := filepath.Dir(doc.Path)
context, err := core.NewLinkFormatterContext(note, notebook.Path, currentDir)
path := core.NotebookPath{
Path: note.Path,
BasePath: notebook.Path,
WorkingDir: filepath.Dir(doc.Path),
}
context, err := core.NewLinkFormatterContext(path, note.Title, note.Metadata)
if err != nil {
return nil, err
}

@ -1,6 +1,7 @@
package term
import (
"fmt"
"os"
"strings"
@ -10,7 +11,8 @@ import (
// Terminal offers utilities to interact with the terminal.
type Terminal struct {
NoInput bool
NoInput bool
ForceInput string
}
func New() *Terminal {
@ -38,7 +40,14 @@ func (t *Terminal) SupportsUTF8() bool {
// Confirm is a shortcut to prompt a yes/no question to the user.
func (t *Terminal) Confirm(msg string, defaultAnswer bool) (confirmed, skipped bool) {
if !t.IsInteractive() {
return defaultAnswer, true
switch strings.ToLower(t.ForceInput) {
case "y":
return t.forceConfirm(msg, true)
case "n":
return t.forceConfirm(msg, false)
default:
return defaultAnswer, true
}
}
confirmed = false
@ -49,3 +58,16 @@ func (t *Terminal) Confirm(msg string, defaultAnswer bool) (confirmed, skipped b
survey.AskOne(prompt, &confirmed)
return confirmed, false
}
func (t *Terminal) forceConfirm(msg string, answer bool) (confirmed, skipped bool) {
msg = "? " + msg + " ("
if answer {
msg += "Y/n"
} else {
msg += "y/N"
}
msg += ")"
fmt.Println(msg)
return answer, false
}

@ -10,8 +10,8 @@ import (
// Index indexes the content of all the notes in the notebook.
type Index struct {
Force bool `short:"f" help:"Force indexing all the notes."`
Verbose bool `short:"v" help:"Print detailed information about the indexing process."`
Quiet bool `short:"q" help:"Do not print statistics nor progress."`
Verbose bool `short:"v" xor:"print" help:"Print detailed information about the indexing process."`
Quiet bool `short:"q" xor:"print" help:"Do not print statistics nor progress."`
}
func (cmd *Index) Help() string {

@ -168,13 +168,13 @@ Created: {{date created "short"}}
"long": `{{style "title" title}} {{style "path" path}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
Modified: {{date modified "short"}}
{{list snippets}}`,
"full": `{{style "title" title}} {{style "path" path}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
Modified: {{date modified "short"}}
Tags: {{join tags ", "}}
{{prepend " " body}}

@ -38,13 +38,13 @@ Created: {{date created "short"}}
test("long", `{{style "title" title}} {{style "path" path}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
Modified: {{date modified "short"}}
{{list snippets}}`)
test("full", `{{style "title" title}} {{style "path" path}}
Created: {{date created "short"}}
Modified: {{date created "short"}}
Modified: {{date modified "short"}}
Tags: {{join tags ", "}}
{{prepend " " body}}

@ -13,14 +13,13 @@ type LSP struct {
func (cmd *LSP) Run(container *cli.Container) error {
server := lsp.NewServer(lsp.ServerOpts{
Name: "zk",
Version: container.Version,
Logger: container.Logger,
LogFile: opt.NewNotEmptyString(cmd.Log),
Notebooks: container.Notebooks,
NoteContentParser: container.NoteContentParser,
TemplateLoader: container.TemplateLoader,
FS: container.FS,
Name: "zk",
Version: container.Version,
Logger: container.Logger,
LogFile: opt.NewNotEmptyString(cmd.Log),
Notebooks: container.Notebooks,
TemplateLoader: container.TemplateLoader,
FS: container.FS,
})
return server.Run()

@ -9,6 +9,7 @@ import (
"github.com/mickael-menu/zk/internal/cli"
"github.com/mickael-menu/zk/internal/core"
dateutil "github.com/mickael-menu/zk/internal/util/date"
"github.com/mickael-menu/zk/internal/util/opt"
osutil "github.com/mickael-menu/zk/internal/util/os"
)
@ -17,6 +18,7 @@ import (
type New struct {
Directory string `arg optional default:"." help:"Directory in which to create the note."`
Title string `short:t placeholder:TITLE help:"Title of the new note."`
Date string ` placeholder:DATE help:"Set the current date."`
Group string `short:g placeholder:NAME help:"Name of the config group this note belongs to. Takes precedence over the config of the directory."`
Extra map[string]string ` help:"Extra variables passed to the templates." mapsep:","`
Template string ` placeholder:PATH help:"Custom template used to render the note."`
@ -35,6 +37,14 @@ func (cmd *New) Run(container *cli.Container) error {
return err
}
date := time.Now()
if cmd.Date != "" {
date, err = dateutil.TimeFromNatural(cmd.Date)
if err != nil {
return err
}
}
note, err := notebook.NewNote(core.NewNoteOpts{
Title: opt.NewNotEmptyString(cmd.Title),
Content: content.Unwrap(),
@ -42,7 +52,7 @@ func (cmd *New) Run(container *cli.Container) error {
Group: opt.NewNotEmptyString(cmd.Group),
Template: opt.NewNotEmptyString(cmd.Template),
Extra: cmd.Extra,
Date: time.Now(),
Date: date,
DryRun: cmd.DryRun,
})

@ -31,10 +31,10 @@ type Container struct {
Version string
Config core.Config
Logger *util.ProxyLogger
Styler *core.ProxyStyler
Terminal *term.Terminal
FS *fs.FileStorage
TemplateLoader core.TemplateLoader
NoteContentParser core.NoteContentParser
WorkingDir string
Notebooks *core.NotebookStore
currentNotebook *core.Notebook
@ -45,7 +45,7 @@ func NewContainer(version string) (*Container, error) {
wrap := errors.Wrapper("initialization")
term := term.New()
styler := term
styler := core.NewProxyStyler(term)
logger := util.NewProxyLogger(util.NewStdLogger("zk: ", 0))
fs, err := fs.NewFileStorage("", logger)
config := core.NewDefaultConfig()
@ -71,23 +71,14 @@ func NewContainer(version string) (*Container, error) {
}
}
noteContentParser := markdown.NewParser(
markdown.ParserOpts{
HashtagEnabled: config.Format.Markdown.Hashtags,
MultiWordTagEnabled: config.Format.Markdown.MultiwordTags,
ColontagEnabled: config.Format.Markdown.ColonTags,
},
logger,
)
return &Container{
Version: version,
Config: config,
Logger: logger,
Terminal: term,
FS: fs,
TemplateLoader: templateLoader,
NoteContentParser: noteContentParser,
Version: version,
Config: config,
Logger: logger,
Styler: styler,
Terminal: term,
FS: fs,
TemplateLoader: templateLoader,
Notebooks: core.NewNotebookStore(config, core.NotebookStorePorts{
FS: fs,
TemplateLoader: templateLoader,
@ -99,8 +90,15 @@ func NewContainer(version string) (*Container, error) {
}
notebook := core.NewNotebook(path, config, core.NotebookPorts{
NoteIndex: sqlite.NewNoteIndex(db, logger),
NoteContentParser: noteContentParser,
NoteIndex: sqlite.NewNoteIndex(db, logger),
NoteContentParser: markdown.NewParser(
markdown.ParserOpts{
HashtagEnabled: config.Format.Markdown.Hashtags,
MultiWordTagEnabled: config.Format.Markdown.MultiwordTags,
ColontagEnabled: config.Format.Markdown.ColonTags,
},
logger,
),
TemplateLoaderFactory: func(language string) (core.TemplateLoader, error) {
loader := handlebars.NewLoader(handlebars.LoaderOpts{
LookupPaths: []string{

@ -3,7 +3,6 @@ package core
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/mickael-menu/zk/internal/util/errors"
@ -13,32 +12,31 @@ import (
// Metadata used to generate a link.
type LinkFormatterContext struct {
// Filename of the note
Filename string
// File path to the note, relative to the notebook root.
Path string
Filename string `json:"filename"`
// File path to the note, relative to the notebook root.link-encode-path
Path string `json:"path"`
// Absolute file path to the note.
AbsPath string `handlebars:"abs-path"`
AbsPath string `json:"absPath" handlebars:"abs-path"`
// File path to the note, relative to the current directory.
RelPath string `handlebars:"rel-path"`
RelPath string `json:"relPath" handlebars:"rel-path"`
// Title of the note.
Title string
Title string `json:"title"`
// Metadata extracted from the YAML frontmatter.
Metadata map[string]interface{}
Metadata map[string]interface{} `json:"metadata"`
}
func NewLinkFormatterContext(note MinimalNote, notebookDir string, currentDir string) (LinkFormatterContext, error) {
absPath := filepath.Join(notebookDir, note.Path)
relPath, err := filepath.Rel(currentDir, absPath)
func NewLinkFormatterContext(path NotebookPath, title string, metadata map[string]interface{}) (LinkFormatterContext, error) {
relPath, err := path.PathRelToWorkingDir()
if err != nil {
return LinkFormatterContext{}, err
}
return LinkFormatterContext{
Filename: filepath.Base(note.Path),
Path: note.Path,
AbsPath: absPath,
Filename: path.Filename(),
Path: path.Path,
AbsPath: path.AbsPath(),
RelPath: relPath,
Title: note.Title,
Metadata: note.Metadata,
Title: title,
Metadata: metadata,
}, nil
}

@ -3,7 +3,6 @@ package core
import (
"encoding/json"
"fmt"
"path/filepath"
"regexp"
"time"
)
@ -18,12 +17,12 @@ func newNoteFormatter(basePath string, template Template, linkFormatter LinkForm
}
return func(note ContextualNote) (string, error) {
path, err := fs.Rel(filepath.Join(basePath, note.Path))
if err != nil {
return "", err
path := NotebookPath{
Path: note.Path,
BasePath: basePath,
WorkingDir: fs.WorkingDir(),
}
absPath, err := fs.Abs(filepath.Join(basePath, note.Path))
relPath, err := path.PathRelToWorkingDir()
if err != nil {
return "", err
}
@ -36,17 +35,15 @@ func newNoteFormatter(basePath string, template Template, linkFormatter LinkForm
return template.Render(noteFormatRenderContext{
Filename: note.Filename(),
FilenameStem: note.FilenameStem(),
Path: path,
AbsPath: absPath,
Path: relPath,
AbsPath: path.AbsPath(),
Title: note.Title,
Link: newLazyStringer(func() string {
link, _ := linkFormatter(LinkFormatterContext{
Path: note.Path,
RelPath: path,
AbsPath: absPath,
Title: note.Title,
Metadata: note.Metadata,
})
context, err := NewLinkFormatterContext(path, note.Title, note.Metadata)
if err != nil {
return ""
}
link, _ := linkFormatter(context)
return link
}),
Lead: note.Lead,

@ -61,7 +61,7 @@ func (n *Notebook) ParseNoteWithContent(absPath string, content []byte) (*Note,
}
contentStr := string(content)
contentParts, err := n.parser.ParseNoteContent(contentStr)
contentParts, err := n.Parser.ParseNoteContent(contentStr)
if err != nil {
return nil, wrap(err)
}

@ -18,9 +18,9 @@ import (
type Notebook struct {
Path string
Config Config
Parser NoteContentParser
index NoteIndex
parser NoteContentParser
templateLoaderFactory TemplateLoaderFactory
idGeneratorFactory IDGeneratorFactory
fs FileStorage
@ -37,8 +37,8 @@ func NewNotebook(
return &Notebook{
Path: path,
Config: config,
Parser: ports.NoteContentParser,
index: ports.NoteIndex,
parser: ports.NoteContentParser,
templateLoaderFactory: ports.TemplateLoaderFactory,
idGeneratorFactory: ports.IDGeneratorFactory,
fs: ports.FS,

@ -0,0 +1,32 @@
package core
import "path/filepath"
type NotebookPath struct {
// Path of a notebook file, relative to the notebook dir.
Path string
// Root directory of the notebook.
BasePath string
// Current working directory.
WorkingDir string
}
// Filename returns the filename of the notebook file.
func (p NotebookPath) Filename() string {
return filepath.Base(p.Path)
}
// AbsPath returns the absolute path to the notebook file.
func (p NotebookPath) AbsPath() string {
return filepath.Join(p.BasePath, p.Path)
}
// PathRelToWorkingDir returns the path to the notebook file relative to the
// working dir. If the working dir is not set, returns the path relative to the
// notebook dir.
func (p NotebookPath) PathRelToWorkingDir() (string, error) {
if p.WorkingDir == "" {
return p.Path, nil
}
return filepath.Rel(p.WorkingDir, p.AbsPath())
}

@ -1,5 +1,7 @@
package core
import "fmt"
// Style is a key representing a single styling rule.
type Style string
@ -73,6 +75,26 @@ type Styler interface {
MustStyle(text string, rules ...Style) string
}
// ProxyStyler is a styler delegating to an underlying styler.
// Can be used to change the active styler during runtime.
type ProxyStyler struct {
Styler Styler
}
func NewProxyStyler(styler Styler) *ProxyStyler {
return &ProxyStyler{styler}
}
// Style implements Styler.
func (s ProxyStyler) Style(text string, rule ...Style) (string, error) {
return s.Styler.Style(text, rule...)
}
// MustStyle implements Styler.
func (s ProxyStyler) MustStyle(text string, rule ...Style) string {
return s.Styler.MustStyle(text, rule...)
}
// NullStyler is a Styler with no styling rules.
var NullStyler = nullStyler{}
@ -87,3 +109,21 @@ func (s nullStyler) Style(text string, rule ...Style) (string, error) {
func (s nullStyler) MustStyle(text string, rule ...Style) string {
return text
}
// TagStyler is a Styler which outputs XML tags.
var TagStyler = tagStyler{}
type tagStyler struct{}
// Style implements Styler.
func (s tagStyler) Style(text string, rules ...Style) (string, error) {
return s.MustStyle(text, rules...), nil
}
// MustStyle implements Styler.
func (s tagStyler) MustStyle(text string, rules ...Style) string {
for _, rule := range rules {
text = fmt.Sprintf("<%s>%s</%s>", rule, text, rule)
}
return text
}

@ -33,7 +33,10 @@ var root struct {
NotebookDir string `type:path placeholder:PATH help:"Turn off notebook auto-discovery and set manually the notebook where commands are run."`
WorkingDir string `short:W type:path placeholder:PATH help:"Run as if zk was started in <PATH> instead of the current working directory."`
NoInput NoInput `help:"Never prompt or ask for confirmation."`
Debug bool `default:"0" help:"Print a debug stacktrace on SIGINT."`
// ForceInput is a debugging flag overriding the default value of interaction prompts.
ForceInput string `hidden xor:"input"`
Debug bool `default:"0" hidden help:"Print a debug stacktrace on SIGINT."`
DebugStyle bool `default:"0" hidden help:"Force styling output as XML tags."`
ShowHelp ShowHelp `cmd hidden default:"1"`
LSP cmd.LSP `cmd hidden`
@ -90,6 +93,11 @@ func main() {
if root.Debug {
setupDebugMode()
}
if root.DebugStyle {
container.Styler.Styler = core.TagStyler
}
container.Terminal.ForceInput = root.ForceInput
// Index the current notebook except if the user is running the `index`
// command, otherwise it would hide the stats.

@ -0,0 +1,56 @@
$ cd edit
# Editor resolution
# Checks that no editor is set in the current environment.
$ echo $VISUAL $EDITOR $ZK_EDITOR
>
# No editor set
1$ zk edit blue.md
2>zk: error: no editor set in config
# Use the EDITOR env variable.
$ EDITOR=echo zk edit blue.md
>{{working-dir}}/blue.md
# VISUAL takes precedence over EDITOR.
$ EDITOR=vim VISUAL=echo zk edit blue.md
>{{working-dir}}/blue.md
# The tool/editor config takes precedence over EDITOR and VISUAL.
$ echo "[tool]\neditor = 'echo'" > .zk/config.toml
$ EDITOR=vim VISUAL=vim zk edit blue.md
>{{working-dir}}/blue.md
# ZK_EDITOR takes precedence over everything else.
$ echo "[tool]\n editor = 'vim'" > .zk/config.toml
$ EDITOR=vim VISUAL=vim ZK_EDITOR=echo zk edit blue.md
>{{working-dir}}/blue.md
# Filtering options
# Sort by title descending
$ ZK_EDITOR=echo zk edit --sort title-
>{{working-dir}}/yellow.md {{working-dir}}/red.md {{working-dir}}/purple.md {{working-dir}}/green.md {{working-dir}}/blue.md
# Edit confirmation
# Opens without confirmation up to 5 notes at the same time.
$ ZK_EDITOR=echo zk edit
>{{working-dir}}/blue.md {{working-dir}}/green.md {{working-dir}}/purple.md {{working-dir}}/red.md {{working-dir}}/yellow.md
# Requires confirmation for more than 5 notes.
$ touch orange.md
$ ZK_EDITOR=echo zk edit --force-input n
>? Are you sure you want to open 6 notes in the editor? (y/N)
1$ ZK_EDITOR=echo zk edit
2>zk: error: too many notes to be opened in the editor, aborting…
# Force confirmation.
$ ZK_EDITOR=echo zk edit --force
>{{working-dir}}/orange.md {{working-dir}}/blue.md {{working-dir}}/green.md {{working-dir}}/purple.md {{working-dir}}/red.md {{working-dir}}/yellow.md

@ -0,0 +1,78 @@
$ cd full-sample
# Print help for `zk graph`
$ zk graph --help
>Usage: zk graph --format=STRING [<path> ...]
>
>Produce a graph of the notes matching the given criteria.
>
>Arguments:
> [<path> ...] Find notes matching the given path, including its descendants.
>
>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.
>
>Formatting
> -f, --format=STRING Format of the graph among: json.
> -q, --quiet Do not print the total number of notes found.
>
>Filtering
> -i, --interactive Select notes interactively with fzf.
> -n, --limit=COUNT Limit the number of notes found.
> -m, --match=QUERY Terms to search for in the notes.
> -e, --exact-match Search for exact occurrences of the --match
> argument (case insensitive).
> -x, --exclude=PATH,... Ignore notes matching the given path, including
> its descendants.
> -t, --tag=TAG,... Find notes tagged with the given tags.
> --mention=PATH,... Find notes mentioning the title of the given
> ones.
> --mentioned-by=PATH,... Find notes whose title is mentioned in the
> given ones.
> -l, --link-to=PATH,... Find notes which are linking to the given ones.
> --no-link-to=PATH,... Find notes which are not linking to the given
> notes.
> -L, --linked-by=PATH,... Find notes which are linked by the given ones.
> --no-linked-by=PATH,... Find notes which are not linked by the given
> ones.
> --orphan Find notes which are not linked by any other
> note.
> --related=PATH,... Find notes which might be related to the given
> ones.
> --max-distance=COUNT Maximum distance between two linked notes.
> -r, --recursive Follow links recursively.
> --created=DATE
> --created-before=DATE Find notes created before the given date.
> --created-after=DATE Find notes created after the given date.
> --modified=DATE Find notes modified on the given date.
> --modified-before=DATE Find notes modified before the given date.
> --modified-after=DATE Find notes modified after the given date.
>
>Sorting
> -s, --sort=TERM,... Order the notes by the given criterion.
# Format is required
1$ zk graph
2>zk: error: missing flags: --format=STRING
# Test the JSON format.
$ zk graph -qn5 --format json
>{
> "notes": [
> {"filename":"uxjt.md","filenameStem":"uxjt","path":"uxjt.md","absPath":"{{working-dir}}/uxjt.md","title":"Buy low, sell high","link":"[Buy low, sell high](uxjt)","lead":"It's better to invest when the prices are low, because it will usually go up on the long term, despite the fact that [financial markets are random](fa2k).","body":"It's better to invest when the prices are low, because it will usually go up on the long term, despite the fact that [financial markets are random](fa2k).\n\nDon't wait until you think the stocks are at their lowest ([speculation](pywo)), instead buy some when the prices are dropping, and buy more every month if the prices continue to drop.\n\nInvesting a constant amount of money regularly (e.g. monthly) is a simple way to make sure you buy less stocks when the prices are high, and more when they are low. [Compound interests will work for you over time](smdc).\n\n:finance:","snippets":["It's better to invest when the prices are low, because it will usually go up on the long term, despite the fact that [financial markets are random](fa2k)."],"rawContent":"# Buy low, sell high\n\nIt's better to invest when the prices are low, because it will usually go up on the long term, despite the fact that [financial markets are random](fa2k).\n\nDon't wait until you think the stocks are at their lowest ([speculation](pywo)), instead buy some when the prices are dropping, and buy more every month if the prices continue to drop.\n\nInvesting a constant amount of money regularly (e.g. monthly) is a simple way to make sure you buy less stocks when the prices are high, and more when they are low. [Compound interests will work for you over time](smdc).\n\n:finance:\n","wordCount":103,"tags":["finance"],"metadata":{},"created":"{{match '[\-T\.\:0-9]+'}}Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"cc0e1a9cad8b526254ac1d87f1534c010c2ffe5d399a7c1af1da636a734b60c2"},
> {"filename":"fwsj.md","filenameStem":"fwsj","path":"fwsj.md","absPath":"{{working-dir}}/fwsj.md","title":"Channel","link":"[Channel](fwsj)","lead":"* Channels are a great approach for safe concurrency.\n* It's an implementation of the [message passing](4oma) pattern.","body":"* Channels are a great approach for safe concurrency.\n* It's an implementation of the [message passing](4oma) pattern.\n\n:programming:","snippets":["* Channels are a great approach for safe concurrency.\n* It's an implementation of the [message passing](4oma) pattern."],"rawContent":"# Channel\n\n* Channels are a great approach for safe concurrency.\n* It's an implementation of the [message passing](4oma) pattern.\n\n:programming:\n","wordCount":21,"tags":["programming"],"metadata":{},"created":"{{match '[\-T\.\:0-9]+'}}Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"cafbb0c69c39729a2e7da6800c97fc5a1f1caa5667ab04c11e06a749610ca4e4"},
> {"filename":"smdc.md","filenameStem":"smdc","path":"smdc.md","absPath":"{{working-dir}}/smdc.md","title":"Compound interests make you rich","link":"[Compound interests make you rich](smdc)","lead":"Since the growth is exponential, time is more important than the amount of money you invest with compound interests. Start investing right now!","body":"Since the growth is exponential, time is more important than the amount of money you invest with compound interests. Start investing right now!\n\nThis also means that small interest percentages add up to big amount. So [beware of financial products](4yib) eating your interests.\n\nBuy new shares with the interests to benefit from the compound interests, e.g. after a unique investment of $1,000 with a 10% interest rate:\n\n- without reinvesting the dividends:\n\t- 40 yrs = $5,000\n\t- 50 yrs = $6,000\n\t\n- with compound interest:\n\t- 40 yrs = $45,000\n\t- 50 yrs = $117,000\n\t\n## References\n\n- [These 3 Charts Show The Amazing Power Of Compound Interest](https://www.businessinsider.com/personal-finance/amazing-power-of-compound-interest-2014-7?r=DE\u0026IR=T)\n\n:finance:","snippets":["Since the growth is exponential, time is more important than the amount of money you invest with compound interests. Start investing right now!"],"rawContent":"# Compound interests make you rich\n\nSince the growth is exponential, time is more important than the amount of money you invest with compound interests. Start investing right now!\n\nThis also means that small interest percentages add up to big amount. So [beware of financial products](4yib) eating your interests.\n\nBuy new shares with the interests to benefit from the compound interests, e.g. after a unique investment of $1,000 with a 10% interest rate:\n\n- without reinvesting the dividends:\n\t- 40 yrs = $5,000\n\t- 50 yrs = $6,000\n\t\n- with compound interest:\n\t- 40 yrs = $45,000\n\t- 50 yrs = $117,000\n\t\n## References\n\n- [These 3 Charts Show The Amazing Power Of Compound Interest](https://www.businessinsider.com/personal-finance/amazing-power-of-compound-interest-2014-7?r=DE\u0026IR=T)\n\n:finance:\n","wordCount":116,"tags":["finance"],"metadata":{},"created":"{{match '[\-T\.\:0-9]+'}}Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"c14982f5c20b58fdbbdcf6430308ee732ebd04b4c4814ded011698d12d0aff6b"},
> {"filename":"g7qa.md","filenameStem":"g7qa","path":"g7qa.md","absPath":"{{working-dir}}/g7qa.md","title":"Concurrency in Rust","link":"[Concurrency in Rust](g7qa)","lead":"* Thanks to the [Ownership pattern](88el), Rust has a model of [Fearless concurrency](2cl7).\n* Rust aims to have a small runtime, so it doesn't support [green threads](inbox/my59).\n * Crates exist to add support for green threads if needed.\n * Instead, Rust relies on the OS threads, a model called 1-1.","body":"* Thanks to the [Ownership pattern](88el), Rust has a model of [Fearless concurrency](2cl7).\n* Rust aims to have a small runtime, so it doesn't support [green threads](inbox/my59).\n * Crates exist to add support for green threads if needed.\n * Instead, Rust relies on the OS threads, a model called 1-1.\n\n* Rust offers a number of constructs for sharing data between threads:\n * [Channel](fwsj) for a safe [message passing](4oma) approach.\n * [Mutex](inbox/er4k) for managing shared state.\n\n:rust:programming:","snippets":["* Thanks to the [Ownership pattern](88el), Rust has a model of [Fearless concurrency](2cl7).\n* Rust aims to have a small runtime, so it doesn't support [green threads](inbox/my59).\n * Crates exist to add support for green threads if needed.\n * Instead, Rust relies on the OS threads, a model called 1-1."],"rawContent":"# Concurrency in Rust\n\n* Thanks to the [Ownership pattern](88el), Rust has a model of [Fearless concurrency](2cl7).\n* Rust aims to have a small runtime, so it doesn't support [green threads](inbox/my59).\n * Crates exist to add support for green threads if needed.\n * Instead, Rust relies on the OS threads, a model called 1-1.\n\n* Rust offers a number of constructs for sharing data between threads:\n * [Channel](fwsj) for a safe [message passing](4oma) approach.\n * [Mutex](inbox/er4k) for managing shared state.\n\n:rust:programming:\n","wordCount":81,"tags":["programming","rust"],"metadata":{},"created":"{{match '[\-T\.\:0-9]+'}}Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"03be1317b6917839ca3a6d1f8c60eab97086cfc2f4637f95f122522476ed0155"},
> {"filename":"3cut.md","filenameStem":"3cut","path":"3cut.md","absPath":"{{working-dir}}/3cut.md","title":"Dangling pointers","link":"[Dangling pointers](3cut)","lead":"A *dangling pointer* is a reference that is kept to freed data. With C, reading it causes a *segmentation fault*.","body":"A *dangling pointer* is a reference that is kept to freed data. With C, reading it causes a *segmentation fault*.\n\nRust protects against *dangling pointers* by making sure data is not freed until it goes out of scope ([Ownership in Rust](88el)).\n\n:programming:","snippets":["A *dangling pointer* is a reference that is kept to freed data. With C, reading it causes a *segmentation fault*."],"rawContent":"---\naliases: [dangling reference]\n---\n\n# Dangling pointers\n\nA *dangling pointer* is a reference that is kept to freed data. With C, reading it causes a *segmentation fault*.\n\nRust protects against *dangling pointers* by making sure data is not freed until it goes out of scope ([Ownership in Rust](88el)).\n\n:programming:\n","wordCount":50,"tags":["programming"],"metadata":{"aliases":["dangling reference"]},"created":"{{match '[\-T\.\:0-9]+'}}Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"7f4a61afdbc077e286c5e0ac91a71bfdec45b6b0cf3a5e14408aba45bd4d58a8"}
> ],
> "links": [
> {"title":"Channel","href":"fwsj","type":"markdown","isExternal":false,"rels":[],"snippet":"[Channel](fwsj) for a safe [message passing](4oma) approach.","snippetStart":423,"snippetEnd":483,"sourceId":11,"sourcePath":"g7qa.md","targetId":10,"targetPath":"fwsj.md"},
> {"title":"Compound interests will work for you over time","href":"smdc","type":"markdown","isExternal":false,"rels":[],"snippet":"Investing a constant amount of money regularly (e.g. monthly) is a simple way to make sure you buy less stocks when the prices are high, and more when they are low. [Compound interests will work for you over time](smdc).","snippetStart":364,"snippetEnd":584,"sourceId":25,"sourcePath":"uxjt.md","targetId":22,"targetPath":"smdc.md"}
> ]
>}

@ -0,0 +1,128 @@
$ cd index
# Print help for `zk index`
$ zk index --help
>Usage: zk index
>
>Index the notes to be searchable.
>
>You usually do not need to run `zk index` manually, as notes are indexed
>automatically when needed.
>
>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.
>
> -f, --force Force indexing all the notes.
> -v, --verbose Print detailed information about the indexing
> process.
> -q, --quiet Do not print statistics nor progress.
# Index initial notes.
$ zk index
>Indexed 3 notes in 0s
> + 0 added
> ~ 3 modified
> - 1 removed
# No changes.
$ zk index
>Indexed 3 notes in 0s
> + 0 added
> ~ 0 modified
> - 0 removed
# Add a new note.
$ touch eggplant/apple.md && zk index
>Indexed 4 notes in 0s
> + 1 added
> ~ 0 modified
> - 0 removed
# Modify an existing note.
$ echo "More" >> banana.md && zk index
>Indexed 4 notes in 0s
> + 0 added
> ~ 1 modified
> - 0 removed
# Delete a note.
$ rm banana.md
$ zk index
>Indexed 3 notes in 0s
> + 0 added
> ~ 0 modified
> - 1 removed
# Ignore path patterns.
$ touch carrot-ignored/ananas.md && zk index
>Indexed 3 notes in 0s
> + 0 added
> ~ 0 modified
> - 0 removed
# Ignore unknown extensions.
$ touch orange.markdown && zk index
>Indexed 3 notes in 0s
> + 0 added
> ~ 0 modified
> - 0 removed
# Force re-indexing all notes.
$ zk index --force
>Indexed 3 notes in 0s
> + 0 added
> ~ 3 modified
> - 0 removed
# Force re-indexing all notes (short flag).
$ zk index -f
>Indexed 3 notes in 0s
> + 0 added
> ~ 3 modified
> - 0 removed
# Quiet mode.
$ zk index --quiet
# Quiet mode (short flag).
$ zk index -q
# Verbose mode.
$ touch banana.md && echo "More" >> litchee.md && rm eggplant/apple.md && zk index --verbose
>- added banana.md
>- removed eggplant/apple.md
>- unchanged eggplant/clementine.md
>- modified litchee.md
>- ignored carrot-ignored/ananas.md: matched ignore glob "carrot-ignored/*"
>- ignored carrot-ignored/tomato.md: matched ignore glob "carrot-ignored/*"
>- ignored orange.markdown: expected extension "md"
>
>Indexed 3 notes in 0s
> + 1 added
> ~ 1 modified
> - 1 removed
# Verbose mode (short flag).
$ zk index -v
>- unchanged banana.md
>- unchanged eggplant/clementine.md
>- unchanged litchee.md
>- ignored carrot-ignored/ananas.md: matched ignore glob "carrot-ignored/*"
>- ignored carrot-ignored/tomato.md: matched ignore glob "carrot-ignored/*"
>- ignored orange.markdown: expected extension "md"
>
>Indexed 3 notes in 0s
> + 0 added
> ~ 0 modified
> - 0 removed
# Verbose and quiet can't be used together.
1$ zk index --verbose --quiet
2>zk: error: --verbose and --quiet can't be used together

@ -0,0 +1,208 @@
$ zk init --no-input > /dev/null
# Test default config.
$ cat .zk/config.toml
># zk configuration file
>#
># Uncomment the properties you want to customize.
>
># NOTE SETTINGS
>#
># Defines the default options used when generating new notes.
>[note]
>
># Language used when writing notes.
># This is used to generate slugs or with date formats.
>#language = "en"
>
># The default title used for new note, if no `--title` flag is provided.
>#default-title = "Untitled"
>
># Template used to generate a note's filename, without extension.
>#filename = "\{{id}}"
>
># The file extension used for the notes.
>#extension = "md"
>
># Template used to generate a note's content.
># If not an absolute path, it is relative to .zk/templates/
>template = "default.md"
>
># Path globs ignored while indexing existing notes.
>#ignore = [
># "drafts/*",
># "log.md"
>#]
>
># Configure random ID generation.
>
># The charset used for random IDs. You can use:
># * letters: only letters from a to z.
># * numbers: 0 to 9
># * alphanum: letters + numbers
># * hex: hexadecimal, from a to f and 0 to 9
># * custom string: will use any character from the provided value
>#id-charset = "alphanum"
>
># Length of the generated IDs.
>#id-length = 4
>
># Letter case for the random IDs, among lower, upper or mixed.
>#id-case = "lower"
>
>
># EXTRA VARIABLES
>#
># A dictionary of variables you can use for any custom values when generating
># new notes. They are accessible in templates with \{{extra.<key>}}
>[extra]
>
>#key = "value"
>
>
># GROUP OVERRIDES
>#
># You can override global settings from [note] and [extra] for a particular
># group of notes by declaring a [group."<name>"] section.
>#
># Specify the list of directories which will automatically belong to the group
># with the optional `paths` property.
>#
># Omitting `paths` is equivalent to providing a single path equal to the name of
># the group. This can be useful to quickly declare a group by the name of the
># directory it applies to.
>
>#[group."<NAME>"]
>#paths = ["<DIR1>", "<DIR2>"]
>#[group."<NAME>".note]
>#filename = "\{{date now}}"
>#[group."<NAME>".extra]
>#key = "value"
>
>
># MARKDOWN SETTINGS
>[format.markdown]
>
># Format used to generate links between notes.
># Either "wiki", "markdown" or a custom template. Default is "markdown".
>link-format = "wiki"
># Indicates whether a link's path will be percent-encoded.
># Defaults to true for "markdown" format and false for "wiki" format.
>#link-encode-path = true
># Indicates whether a link's path file extension will be removed.
># Defaults to true.
>#link-drop-extension = true
>
># Enable support for #hashtags.
>hashtags = true
># Enable support for :colon:separated:tags:.
>colon-tags = false
># Enable support for Bear's #multi-word tags#
># Hashtags must be enabled for multi-word tags to work.
>multiword-tags = false
>
>
># EXTERNAL TOOLS
>[tool]
>
># Default editor used to open notes. When not set, the EDITOR or VISUAL
># environment variables are used.
>#editor = "vim"
>
># Pager used to scroll through long output. If you want to disable paging
># altogether, set it to an empty string "".
>#pager = "less -FIRX"
>
># Command used to preview a note during interactive fzf mode.
># Set it to an empty string "" to disable preview.
>
># bat is a great tool to render Markdown document with syntax highlighting.
>#https://github.com/sharkdp/bat
>#fzf-preview = "bat -p --color always {-1}"
>
>
># LSP
>#
># Configure basic editor integration for LSP-compatible editors.
># See https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md
>#
>[lsp]
>
>[lsp.diagnostics]
># Each diagnostic can have for value: none, hint, info, warning, error
>
># Report titles of wiki-links as hints.
>#wiki-title = "hint"
># Warn for dead links between notes.
>dead-link = "error"
>
>[lsp.completion]
># Customize the completion pop-up of your LSP client.
>
># Show the note title in the completion pop-up, or fallback on its path if empty.
>#note-label = ""
># Filter out the completion pop-up using the note title or its path.
>#note-filter-text = " "
># Show the note filename without extension as detail.
>#note-detail = ""
>
>
># NAMED FILTERS
>#
># A named filter is a set of note filtering options used frequently together.
>#
>[filter]
>
># Matches the notes created the last two weeks. For example:
># $ zk list recents --limit 15
># $ zk edit recents --interactive
>#recents = "--sort created- --created-after 'last two weeks'"
>
>
># COMMAND ALIASES
>#
># Aliases are user commands called with `zk <alias> [<flags>] [<args>]`.
>#
># The alias will be executed with `$SHELL -c`, please refer to your shell's
># man page to see the available syntax. In most shells:
># * $@ can be used to expand all the provided flags and arguments
># * you can pipe commands together with the usual | character
>#
>[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"'
# Test default template.
$ cat .zk/templates/default.md
># \{{title}}
>
>\{{content}}

@ -0,0 +1,42 @@
# Print help for `zk init`
$ zk init --help
>Usage: zk init [<directory>]
>
>Create a new notebook in the given directory.
>
>Arguments:
> [<directory>] Directory containing the notebook.
>
>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.
# Creates a new notebook in a new directory.
$ zk init --no-input new-dir
>
>Initialized a notebook in {{working-dir}}/new-dir
$ test -f new-dir/.zk/config.toml
# Creates a new notebook in an existing directory.
$ mkdir existing-dir
$ zk init --no-input existing-dir
>
>Initialized a notebook in {{working-dir}}/existing-dir
$ test -f existing-dir/.zk/config.toml
# Creates a new notebook in the current directory.
$ mkdir cur-dir && cd cur-dir
$ zk init --no-input
>
>Initialized a notebook in {{working-dir}}
$ test -f .zk/config.toml

@ -0,0 +1,98 @@
$ cd full-sample
# List a note created on a given day.
$ zk list -qf\{{title}} --created 2011-05-16T09:58:57Z
>When to prefer PUT over POST HTTP method?
# List notes created today.
$ zk list -qf\{{title}} --created today
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?
# List notes created before a given date.
$ zk list -qf\{{title}} --created-before "2 weeks ago"
>When to prefer PUT over POST HTTP method?
# List notes created after a given date.
$ zk list -qf\{{title}} --created-after "2 weeks ago"
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?
# List notes modified today.
$ zk list -qf\{{title}} --modified today
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?

@ -0,0 +1,152 @@
$ cd full-sample
# Find notes linking to "Message passing".
$ zk list --debug-style -q --link-to 4oma.md
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - [Channel](fwsj) for a safe [<term>message passing</term>](4oma) approach.
>
# Follow backlinks of "Message passing" recursively.
# FIXME: The snippets don't seem right.
$ zk list --debug-style -q --link-to 4oma.md --recursive
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
> - [Channel](fwsj) for a safe [<term>message passing</term>](4oma) approach.
>
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
# Follow backlinks of "Message passing" recursively, limiting to 1 level of indirection.
$ zk list --debug-style -q --link-to 4oma.md --recursive --max-distance 1
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - [Channel](fwsj) for a safe [<term>message passing</term>](4oma) approach.
>
# Find notes linked by "Mutex".
# The snippet is an extract from the Mutex note.
$ zk list --debug-style -q --linked-by inbox/er4k.m
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
>
><title>Ownership in Rust</title> <path>88el.md</path> (just now)
>
> - Thanks to its [<term>Ownership</term>](../88el) pattern, Rust makes sure we can't mess up when using locks.
>
# Find notes linked by "Mutex" recursively.
$ zk list --debug-style -q --linked-by inbox/er4k.m --recursive
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
>
><title>Ownership in Rust</title> <path>88el.md</path> (just now)
>
> - Thanks to its [<term>Ownership</term>](../88el) pattern, Rust makes sure we can't mess up when using locks.
>
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
><title>The Stack and the Heap</title> <path>tdrj.md</path> (just now)
>
> - The Ownership is a method to manage data on the Heap ([<term>The Stack and the Heap</term>](tdrj)).
>
><title>Do not communicate by sharing memory; instead, share memory by communicating</title> <path>ref/7fto.md</path> (just now)
>
> - The Go language is advocating for this approach with their slogan: "[<term>Do not communicate by sharing memory; instead, share memory by communicating</term>](ref/7fto)".
>
# Find notes linked by "Mutex" recursively, limiting to 2 levels.
$ zk list --debug-style -q --linked-by inbox/er4k.m --recursive --max-distance 2
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
>
><title>Ownership in Rust</title> <path>88el.md</path> (just now)
>
> - Thanks to its [<term>Ownership</term>](../88el) pattern, Rust makes sure we can't mess up when using locks.
>
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - It's an implementation of the [<term>message passing</term>](4oma) pattern.
>
><title>The Stack and the Heap</title> <path>tdrj.md</path> (just now)
>
> - The Ownership is a method to manage data on the Heap ([<term>The Stack and the Heap</term>](tdrj)).
>
# Find notes not linking to "Channel".
$ zk list -qf\{{title}} --no-link-to fwsj.md
>Buy low, sell high
>Channel
>Compound interests make you rich
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?
# Find notes not linked by "Channel".
$ zk list -qf\{{title}} --no-linked-by fwsj.md
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?

@ -0,0 +1,126 @@
$ cd full-sample
# Search for a multi-word term.
$ zk list -q --debug-style --match '"green thread"'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green threads</term>, and languages that use these <term>green threads</term> will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …so it doesn't support [<term>green threads</term>](inbox/my59).
> * Crates exist to add support for <term>green threads</term> if needed.
> * Instead, Rust relies…
>
# Search for two terms (defaults to AND).
$ zk list -q --debug-style --match 'green channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with explicit AND.
$ zk list -q --debug-style --match 'green AND channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with OR.
$ zk list -q --debug-style --match 'green OR channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - * <term>Channels</term> are a great approach for safe concurrency.
> * It's an implementation of the [message passing](4oma) pattern.
>
> :programming:
>
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - * A popular approach for safe concurrency is to use *message passing* instead of shared state.
> * <term>Channels</term> are an example of…
>
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - …with a *locking system*.
> * Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
> * The main risk is to…
>
# Exclude a term.
$ zk list -q --debug-style --match 'green -channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
# Search in the `title` field.
$ zk list -q --debug-style --match 'title:(green thread)'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - > Many operating systems provide an API for creating new threads. This model where a language calls the operating system APIs…
>
# Search with a prefix in `title`.
$ zk list -q --debug-style --match 'title:^do*'
><title>Do not communicate by sharing memory; instead, share memory by communicating</title> <path>ref/7fto.md</path> (just now)
>
> - * Advocates for the use of [message passing](4oma) instead of shared state.
> * A slogan initially coined by Rob Pike ([Effective…
>
><title>Don't speculate</title> <path>pywo.md</path> (just now)
>
> - You have more to loose by doing market timing (jumping in and out of the stocks market). Remember, [the less…
>
# Search with a prefix.
$ zk list -q --debug-style --match 'mut*'
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - * Abbreviation of *<term>mutual</term> exclusion*.
> * An approach to manage safely shared state by allowing only a single thread to access a…
>
><title>Data race error</title> <path>3403.md</path> (just now)
>
> - …Rust prevents *data races* by allowing only a single <term>mutable</term> reference of a value per scope.
>
> :programming:
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …data between threads:
> * [Channel](fwsj) for a safe [message passing](4oma) approach.
> * [<term>Mutex</term>](inbox/er4k) for managing shared state.
>
> :rust:programming:
>
# Search for an exact match.
$ zk list -q --debug-style --exact-match --match '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>
# Search for an exact match (short flag).
$ zk list -q --debug-style -em '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>

@ -0,0 +1,50 @@
$ cd full-sample
# List notes mentioning "Channels" (main title).
$ zk list -q --debug-style --mention fwsj.md
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - * A popular approach for safe concurrency is to use *message passing* instead of shared state.
> * <term>Channels</term> are an example of…
>
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - …with a *locking system*.
> * Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
> * The main risk is to…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …a number of constructs for sharing data between threads:
> * [<term>Channel</term>](fwsj) for a safe [message passing](4oma) approach.
> * [Mutex](inbox/er4k…
>
# List notes mentioning "Dangling pointers" (using alias from the metadata).
$ zk list -q --debug-style --mention 3cut.md
><title>The borrow checker</title> <path>hkvy.md</path> (just now)
>
> - …to the scope of the owned data to prevent <term>dangling references</term>. It also makes sure that the relationship between *lifetimes…
>
# List notes mentioned by "Message passing" (using the main title)
$ zk list -q --debug-style --mentioned-by 4oma.md
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - * A popular approach for safe concurrency is to use *message passing* instead of shared state.
> * <term>Channels</term> are an example of…
>
><title>Do not communicate by sharing memory; instead, share memory by communicating</title> <path>ref/7fto.md</path> (just now)
>
> - …advocating for this approach with their slogan: "[<term>Do not communicate by sharing memory; instead, share memory by communicating</term>](ref/7fto)".
>
> :programming:
>
# List notes mentioned by "The borrow checker" (using alias from the metadata)
$ zk list -q --debug-style --mentioned-by hkvy.md
><title>Dangling pointers</title> <path>3cut.md</path> (just now)
>
> - …to the scope of the owned data to prevent <term>dangling references</term>. It also makes sure that the relationship between *lifetimes…
>

@ -0,0 +1,16 @@
$ cd full-sample
# List notes without any backlinks.
$ zk list -qf"\{{path}} \{{title}}" --orphan
>g7qa.md Concurrency in Rust
>3cut.md Dangling pointers
>3403.md Data race error
>inbox/akwm.md Errors should be handled differently in an application versus a library
>ref/eg7k.md Null references: the billion dollar mistake
>oumc.md Strings are a complicated data structure
>hkvy.md The borrow checker
>wtz9.md Use small Hashable items with diffable data sources
>inbox/dld4.md When to prefer PUT over POST HTTP method?
>zbon.md Zero-cost abstractions in Rust
>18is.md §How to invest in the stock markets?

@ -0,0 +1,160 @@
$ cd full-sample
# Select several notes.
$ zk list -qfpath g7qa.md inbox/akwm.md 3cut.md
>g7qa.md
>3cut.md
>inbox/akwm.md
# Select notes with a path prefix `u`.
$ zk list -qfpath u 4oma.md
>uxjt.md
>4oma.md
>uok6.md
# Select a folder.
$ zk list -qfpath inbox
>inbox/akwm.md
>inbox/my59.md
>inbox/er4k.md
>inbox/dld4.md
# We must select a folder with its full name, not only a prefix.
$ zk list -fpath inb
2>
2>Found 0 note
# Exclude several notes.
# Test combination with , or two flags.
$ zk list -qfpath --exclude g7qa.md,inbox/akwm.md --exclude 3cut.md
>uxjt.md
>fwsj.md
>smdc.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>2cl7.md
>fa2k.md
>inbox/my59.md
>k9bm.md
>4yib.md
>4oma.md
>inbox/er4k.md
>ref/eg7k.md
>88el.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>inbox/dld4.md
>zbon.md
>18is.md
# Exclude with short flag.
$ zk list -qfpath -x inbox,ref,1,2,3,4,5,6,7,8,a,b,c,d,e,f
>uxjt.md
>smdc.md
>g7qa.md
>pywo.md
>k9bm.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>zbon.md
# Exclude is not recursive.
$ zk list -qfpath --exclude .
>ref/7fto.md
>inbox/akwm.md
>inbox/my59.md
>inbox/er4k.md
>ref/eg7k.md
>inbox/dld4.md
# Exclude notes with a path prefix `u`.
$ zk list -qfpath --exclude u,4oma.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>inbox/akwm.md
>2cl7.md
>fa2k.md
>inbox/my59.md
>k9bm.md
>4yib.md
>inbox/er4k.md
>ref/eg7k.md
>88el.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>inbox/dld4.md
>zbon.md
>18is.md
# Exclude a folder.
$ zk list -qfpath --exclude inbox
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>2cl7.md
>fa2k.md
>k9bm.md
>4yib.md
>4oma.md
>ref/eg7k.md
>88el.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>zbon.md
>18is.md
# We must exclude a folder with its full name, not only a prefix.
$ zk list -qfpath --exclude inb
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>inbox/akwm.md
>2cl7.md
>fa2k.md
>inbox/my59.md
>k9bm.md
>4yib.md
>4oma.md
>inbox/er4k.md
>ref/eg7k.md
>88el.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>inbox/dld4.md
>zbon.md
>18is.md

@ -0,0 +1,16 @@
$ cd full-sample
# List notes related to "Buy low, sell high" but not linked by it.
$ zk list -qf"\{{title}}" --related uxjt
>Investment business is a scam
>Stick to your portfolio strategy
# List notes related to "Fearless concurrency" but not linked by it.
$ zk list -qf"\{{title}}" --related 2cl7.md
>The Stack and the Heap
# List notes related to "Null references"
$ zk list -f"\{{title}}" --related ref/eg7k.md
2>
2>Found 0 note

@ -0,0 +1,51 @@
$ cd full-sample
# Filter by a tag.
$ zk list -qf\{{title}} --tag programming
>Channel
>Concurrency in Rust
>Dangling pointers
>Data race error
>Do not communicate by sharing memory; instead, share memory by communicating
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Green threads
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
# Filter with multiple tags.
$ zk list -qf\{{title}} --tag programming,rust
>Concurrency in Rust
>Ownership in Rust
>The borrow checker
>Zero-cost abstractions in Rust
# Filter with any of the given tags.
$ zk list -qf\{{title}} --tag "swift OR http"
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
# Exclude notes with the given tags.
$ zk list -qf\{{title}} --tag "NOT programming"
>Buy low, sell high
>Compound interests make you rich
>Diversify your portfolio
>Don't speculate
>Financial markets are random
>How to choose a broker?
>Investment business is a scam
>Stick to your portfolio strategy
>§How to invest in the stock markets?
# Filter by a tag prefix.
$ zk list -qf\{{title}} --tag "sw*"
>Use small Hashable items with diffable data sources

@ -0,0 +1,74 @@
$ cd full-sample
# Test custom format template.
# JSON output of the template context.
$ zk list -qf "\{{json .}}" inbox/dld4.md
>{"filename":"dld4.md","filenameStem":"dld4","path":"inbox/dld4.md","absPath":"{{working-dir}}/inbox/dld4.md","title":"When to prefer PUT over POST HTTP method?","link":"[When to prefer PUT over POST HTTP method?](inbox/dld4)","lead":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.","body":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`","snippets":["`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again."],"rawContent":"---\ndate: 2011-05-16 09:58:57\nkeywords: [programming, http]\ncategory: \"Best practice\"\n---\n\n# When to prefer PUT over POST HTTP method?\n\n`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`\n","wordCount":66,"tags":["programming","http"],"metadata":{"category":"Best practice","date":"2011-05-16 09:58:57","keywords":["programming","http"]},"created":"2011-05-16T09:58:57Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"8cef4e35473a5ebf29d72b5d0e1bca4471dcf496f4971980840aafe4bf3d2298"}
# Individual Handlebars template variables.
$ zk list -qf "\{{filename}}" inbox/dld4.md
>dld4.md
$ zk list -qf "\{{filename-stem}}" inbox/dld4.md
>dld4
$ zk list -qf "\{{path}}" inbox/dld4.md
>inbox/dld4.md
$ zk list -qf "\{{abs-path}}" inbox/dld4.md
>{{working-dir}}/inbox/dld4.md
$ zk list -qf "\{{title}}" inbox/dld4.md
>When to prefer PUT over POST HTTP method?
$ zk list -qf "\{{link}}" inbox/dld4.md
>[When to prefer PUT over POST HTTP method?](inbox/dld4)
$ zk list -qf "\{{lead}}" inbox/dld4.md
>`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
$ zk list -qf "\{{body}}" inbox/dld4.md
>`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
>A way to see it is:
>
>* `PUT` = SQL `UPDATE`
>* `POST` = SQL `INSERT`
$ zk list -qf "\{{snippets}}" inbox/dld4.md
>`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
$ zk list -qf "\{{raw-content}}" inbox/dld4.md
>---
>date: 2011-05-16 09:58:57
>keywords: [programming, http]
>category: "Best practice"
>---
>
># When to prefer PUT over POST HTTP method?
>
>`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
>A way to see it is:
>
>* `PUT` = SQL `UPDATE`
>* `POST` = SQL `INSERT`
>
$ zk list -qf "\{{word-count}}" inbox/dld4.md
>66
$ zk list -qf "\{{json tags}}" inbox/dld4.md
>["programming","http"]
$ zk list -qf "\{{json metadata}}" inbox/dld4.md
>{"category":"Best practice","date":"2011-05-16 09:58:57","keywords":["programming","http"]}
$ zk list -qf "\{{created}}" inbox/dld4.md
>2011-05-16 09:58:57 +0000 UTC
$ zk list -qf "\{{checksum}}" inbox/dld4.md
>8cef4e35473a5ebf29d72b5d0e1bca4471dcf496f4971980840aafe4bf3d2298

@ -0,0 +1,68 @@
$ cd full-sample
# The default format is `short`.
$ zk list --debug-style -q inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path> ({{match '[0-9]+'}} years ago)
>
> - `PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
# `path` format.
$ zk list --debug-style -qfpath inbox/dld4.md
>inbox/dld4.md
# `link` format.
$ zk list --debug-style -qflink inbox/dld4.md
>[When to prefer PUT over POST HTTP method?](inbox/dld4)
# `oneline` format.
$ zk list --debug-style -qfoneline inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path> ({{match '[0-9]+'}} years ago)
# `short` format.
$ zk list --debug-style -qfshort inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path> ({{match '[0-9]+'}} years ago)
>
> - `PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
# `medium` format.
$ zk list --debug-style -qfmedium inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path>
>Created: 05/16/2011
>
> - `PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
# `long` format.
$ zk list --debug-style -qflong inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path>
>Created: 05/16/2011
>Modified: {{match '[/0-9]+'}}
>
> - `PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
# `full` format.
$ zk list --debug-style -qffull inbox/dld4.md
><title>When to prefer PUT over POST HTTP method?</title> <path>inbox/dld4.md</path>
>Created: 05/16/2011
>Modified: {{match '[/0-9]+'}}
>Tags: programming, http
>
> `PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
>
> A way to see it is:
>
> * `PUT` = SQL `UPDATE`
> * `POST` = SQL `INSERT`
>
# JSON format.
$ zk list -qfjson inbox/dld4.md
>[{"filename":"dld4.md","filenameStem":"dld4","path":"inbox/dld4.md","absPath":"{{working-dir}}/inbox/dld4.md","title":"When to prefer PUT over POST HTTP method?","link":"[When to prefer PUT over POST HTTP method?](inbox/dld4)","lead":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.","body":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`","snippets":["`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again."],"rawContent":"---\ndate: 2011-05-16 09:58:57\nkeywords: [programming, http]\ncategory: \"Best practice\"\n---\n\n# When to prefer PUT over POST HTTP method?\n\n`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`\n","wordCount":66,"tags":["programming","http"],"metadata":{"category":"Best practice","date":"2011-05-16 09:58:57","keywords":["programming","http"]},"created":"2011-05-16T09:58:57Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"8cef4e35473a5ebf29d72b5d0e1bca4471dcf496f4971980840aafe4bf3d2298"}]
# JSON Lines format.
$ zk list -qfjsonl inbox/dld4.md
>{"filename":"dld4.md","filenameStem":"dld4","path":"inbox/dld4.md","absPath":"{{working-dir}}/inbox/dld4.md","title":"When to prefer PUT over POST HTTP method?","link":"[When to prefer PUT over POST HTTP method?](inbox/dld4)","lead":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.","body":"`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`","snippets":["`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again."],"rawContent":"---\ndate: 2011-05-16 09:58:57\nkeywords: [programming, http]\ncategory: \"Best practice\"\n---\n\n# When to prefer PUT over POST HTTP method?\n\n`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.\n\nA way to see it is:\n\n* `PUT` = SQL `UPDATE`\n* `POST` = SQL `INSERT`\n","wordCount":66,"tags":["programming","http"],"metadata":{"category":"Best practice","date":"2011-05-16 09:58:57","keywords":["programming","http"]},"created":"2011-05-16T09:58:57Z","modified":"{{match '[\-T\.\:0-9]+'}}Z","checksum":"8cef4e35473a5ebf29d72b5d0e1bca4471dcf496f4971980840aafe4bf3d2298"}

@ -0,0 +1,60 @@
$ cd full-sample
# Header.
$ zk list -n4 -qfpath --header "HEADER"
>HEADERuxjt.md
>fwsj.md
>smdc.md
>g7qa.md
# Footer.
$ zk list -n4 -qfpath --footer "FOOTER\n"
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.mdFOOTER
# Delimiter.
$ zk list -n4 -qfpath --delimiter ";"
>uxjt.md;fwsj.md;smdc.md;g7qa.md
# Delimiter (short flag).
$ zk list -n4 -qfpath -d,
>uxjt.md,fwsj.md,smdc.md,g7qa.md
# Can't mix --delimiter0 and --delimiter
1$ zk list --delimiter0 --delimiter ","
2>zk: error: --delimiter and --delimiter0 can't be used together
# Can't mix --delimiter0 and --header
1$ zk list --delimiter0 --header "-"
2>zk: error: --footer and --delimiter0 can't be used together
# Can't mix --delimiter0 and --footer
1$ zk list --delimiter0 --footer "-"
2>zk: error: --footer and --delimiter0 can't be used together
# Can't mix --format json and --header
1$ zk list --format json --header "-"
2>zk: error: --header can't be used with JSON format
# Can't mix --format json and --footer
1$ zk list --format json --footer "-"
2>zk: error: --footer can't be used with JSON format
# Can't mix --format json and --delimiter
1$ zk list --format json --delimiter "-"
2>zk: error: --delimiter can't be used with JSON format
# Can't mix --format jsonl and --header
1$ zk list --format jsonl --header "-"
2>zk: error: --header can't be used with JSON format
# Can't mix --format jsonl and --footer
1$ zk list --format jsonl --footer "-"
2>zk: error: --footer can't be used with JSON format
# Can't mix --format jsonl and --delimiter
1$ zk list --format jsonl --delimiter "-"
2>zk: error: --delimiter can't be used with JSON format

@ -0,0 +1,90 @@
$ cd full-sample
# No limit.
$ zk list -fpath
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>inbox/akwm.md
>2cl7.md
>fa2k.md
>inbox/my59.md
>k9bm.md
>4yib.md
>4oma.md
>inbox/er4k.md
>ref/eg7k.md
>88el.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>inbox/dld4.md
>zbon.md
>18is.md
2>
2>Found 27 notes
# Limit.
$ zk list -fpath --limit 5
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
2>
2>Found 5 notes
# Limit with short flag.
$ zk list -fpath -n5
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
2>
2>Found 5 notes
# Invalid limit.
1$ zk list -fpath --limit a
2>zk: error: --limit: expected a valid 64 bit int but got "a"
# Limit to 0 means no limit.
$ zk list -fpath --limit 0
>uxjt.md
>fwsj.md
>smdc.md
>g7qa.md
>3cut.md
>3403.md
>aqfd.md
>ref/7fto.md
>pywo.md
>inbox/akwm.md
>2cl7.md
>fa2k.md
>inbox/my59.md
>k9bm.md
>4yib.md
>4oma.md
>inbox/er4k.md
>ref/eg7k.md
>88el.md
>uok6.md
>oumc.md
>tdrj.md
>hkvy.md
>wtz9.md
>inbox/dld4.md
>zbon.md
>18is.md
2>
2>Found 27 notes

@ -0,0 +1,252 @@
$ cd full-sample
# The default sort order is by title.
$ zk list -qf\{{title}} --sort title
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?
# Sort by unknown order.
1$ zk list -q --sort unknown
2>zk: error: incorrect criteria: unknown: unknown sorting term
2> try created, modified, path, title, random or word-count
# Sort by title (default ascending).
$ zk list -qf\{{title}} --sort title
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
>Dangling pointers
>Data race error
>Diversify your portfolio
>Do not communicate by sharing memory; instead, share memory by communicating
>Don't speculate
>Errors should be handled differently in an application versus a library
>Fearless concurrency
>Financial markets are random
>Green threads
>How to choose a broker?
>Investment business is a scam
>Message passing
>Mutex
>Null references: the billion dollar mistake
>Ownership in Rust
>Stick to your portfolio strategy
>Strings are a complicated data structure
>The Stack and the Heap
>The borrow checker
>Use small Hashable items with diffable data sources
>When to prefer PUT over POST HTTP method?
>Zero-cost abstractions in Rust
>§How to invest in the stock markets?
# Sort by title (shortcut).
$ zk list -qf\{{title}} -st -n4
>Buy low, sell high
>Channel
>Compound interests make you rich
>Concurrency in Rust
# Sort by title descending.
$ zk list -qf\{{title}} --sort title-
>§How to invest in the stock markets?
>Zero-cost abstractions in Rust
>When to prefer PUT over POST HTTP method?
>Use small Hashable items with diffable data sources
>The borrow checker
>The Stack and the Heap
>Strings are a complicated data structure
>Stick to your portfolio strategy
>Ownership in Rust
>Null references: the billion dollar mistake
>Mutex
>Message passing
>Investment business is a scam
>How to choose a broker?
>Green threads
>Financial markets are random
>Fearless concurrency
>Errors should be handled differently in an application versus a library
>Don't speculate
>Do not communicate by sharing memory; instead, share memory by communicating
>Diversify your portfolio
>Data race error
>Dangling pointers
>Concurrency in Rust
>Compound interests make you rich
>Channel
>Buy low, sell high
# Sort by path (default ascending).
$ zk list -qfpath -n4 --sort path
>18is.md
>2cl7.md
>3403.md
>3cut.md
# Sort by path (shortcut).
$ zk list -qfpath -n4 -sp
>18is.md
>2cl7.md
>3403.md
>3cut.md
# Sort by path descending.
$ zk list -qfpath -n4 --sort path-
>zbon.md
>wtz9.md
>uxjt.md
>uok6.md
# Sort by word count (default ascending).
$ zk list -qf"\{{word-count}} \{{title}}" -n4 --sort word-count
>21 Channel
>37 Do not communicate by sharing memory; instead, share memory by communicating
>37 Ownership in Rust
>44 Fearless concurrency
# Sort by word count (shortcut).
$ zk list -qf"\{{word-count}} \{{title}}" -n4 -swc
>21 Channel
>37 Do not communicate by sharing memory; instead, share memory by communicating
>37 Ownership in Rust
>44 Fearless concurrency
# Sort by word count descending.
$ zk list -qf"\{{word-count}} \{{title}}" -n4 --sort word-count-
>196 The Stack and the Heap
>124 Green threads
>120 Stick to your portfolio strategy
>116 Compound interests make you rich
# Sort by creation date (default descending).
$ zk list -qf\{{title}} -n4 --sort created
>Zero-cost abstractions in Rust
>Use small Hashable items with diffable data sources
>Buy low, sell high
>Stick to your portfolio strategy
# Sort by creation date (shortcut).
$ zk list -qf\{{title}} -n4 -sc
>Zero-cost abstractions in Rust
>Use small Hashable items with diffable data sources
>Buy low, sell high
>Stick to your portfolio strategy
# Sort by creation date ascending.
$ zk list -qf\{{title}} -n4 --sort created+
>When to prefer PUT over POST HTTP method?
>§How to invest in the stock markets?
>Fearless concurrency
>Data race error
# Sort by modification date (default descending).
$ zk list -qf\{{title}} -n4 --sort modified
>Zero-cost abstractions in Rust
>Use small Hashable items with diffable data sources
>Buy low, sell high
>Stick to your portfolio strategy
# Sort by modification date (shortcut).
$ zk list -qf\{{title}} -n4 -sm
>Zero-cost abstractions in Rust
>Use small Hashable items with diffable data sources
>Buy low, sell high
>Stick to your portfolio strategy
# Sort by modification date ascending.
$ zk list -qf\{{title}} -n4 --sort modified+
>§How to invest in the stock markets?
>Fearless concurrency
>Data race error
>Dangling pointers
# Sort by random order.
# For practical purpose, checks only that the command doesn't error out.
$ zk list -q --sort random > /dev/null
# Sort by multiple orders.
$ zk list -qf"\{{word-count}} \{{title}}" --sort title-,word-count
>21 Channel
>37 Ownership in Rust
>37 Do not communicate by sharing memory; instead, share memory by communicating
>44 Financial markets are random
>44 Fearless concurrency
>49 Use small Hashable items with diffable data sources
>50 Dangling pointers
>53 Message passing
>60 Zero-cost abstractions in Rust
>66 When to prefer PUT over POST HTTP method?
>67 Errors should be handled differently in an application versus a library
>71 §How to invest in the stock markets?
>76 Mutex
>80 The borrow checker
>80 Data race error
>81 Concurrency in Rust
>95 Strings are a complicated data structure
>98 How to choose a broker?
>98 Diversify your portfolio
>103 Buy low, sell high
>106 Investment business is a scam
>107 Don't speculate
>109 Null references: the billion dollar mistake
>116 Compound interests make you rich
>120 Stick to your portfolio strategy
>124 Green threads
>196 The Stack and the Heap
# Sort by multiple orders (shortcut)
$ zk list -qf"\{{word-count}} \{{title}}" -st-,wc
>21 Channel
>37 Ownership in Rust
>37 Do not communicate by sharing memory; instead, share memory by communicating
>44 Financial markets are random
>44 Fearless concurrency
>49 Use small Hashable items with diffable data sources
>50 Dangling pointers
>53 Message passing
>60 Zero-cost abstractions in Rust
>66 When to prefer PUT over POST HTTP method?
>67 Errors should be handled differently in an application versus a library
>71 §How to invest in the stock markets?
>76 Mutex
>80 The borrow checker
>80 Data race error
>81 Concurrency in Rust
>95 Strings are a complicated data structure
>98 How to choose a broker?
>98 Diversify your portfolio
>103 Buy low, sell high
>106 Investment business is a scam
>107 Don't speculate
>109 Null references: the billion dollar mistake
>116 Compound interests make you rich
>120 Stick to your portfolio strategy
>124 Green threads
>196 The Stack and the Heap

@ -0,0 +1,96 @@
$ cd full-sample
# Print help for `zk list`
$ zk list --help
>Usage: zk list [<path> ...]
>
>List notes matching the given criteria.
>
>Arguments:
> [<path> ...] Find notes matching the given path, including its descendants.
>
>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.
>
>Formatting
> -f, --format=TEMPLATE Pretty print the list using a custom template or one
> of the predefined formats: oneline, short, medium,
> long, 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 notes delimited by the given separator.
> -0, --delimiter0 Print notes 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 notes found.
>
>Filtering
> -i, --interactive Select notes interactively with fzf.
> -n, --limit=COUNT Limit the number of notes found.
> -m, --match=QUERY Terms to search for in the notes.
> -e, --exact-match Search for exact occurrences of the --match
> argument (case insensitive).
> -x, --exclude=PATH,... Ignore notes matching the given path, including
> its descendants.
> -t, --tag=TAG,... Find notes tagged with the given tags.
> --mention=PATH,... Find notes mentioning the title of the given
> ones.
> --mentioned-by=PATH,... Find notes whose title is mentioned in the
> given ones.
> -l, --link-to=PATH,... Find notes which are linking to the given ones.
> --no-link-to=PATH,... Find notes which are not linking to the given
> notes.
> -L, --linked-by=PATH,... Find notes which are linked by the given ones.
> --no-linked-by=PATH,... Find notes which are not linked by the given
> ones.
> --orphan Find notes which are not linked by any other
> note.
> --related=PATH,... Find notes which might be related to the given
> ones.
> --max-distance=COUNT Maximum distance between two linked notes.
> -r, --recursive Follow links recursively.
> --created=DATE
> --created-before=DATE Find notes created before the given date.
> --created-after=DATE Find notes created after the given date.
> --modified=DATE Find notes modified on the given date.
> --modified-before=DATE Find notes modified before the given date.
> --modified-after=DATE Find notes modified after the given date.
>
>Sorting
> -s, --sort=TERM,... Order the notes by the given criterion.
# List all notes.
$ zk list -qf"\{{path}} \{{title}}"
>uxjt.md Buy low, sell high
>fwsj.md Channel
>smdc.md Compound interests make you rich
>g7qa.md Concurrency in Rust
>3cut.md Dangling pointers
>3403.md Data race error
>aqfd.md Diversify your portfolio
>ref/7fto.md Do not communicate by sharing memory; instead, share memory by communicating
>pywo.md Don't speculate
>inbox/akwm.md Errors should be handled differently in an application versus a library
>2cl7.md Fearless concurrency
>fa2k.md Financial markets are random
>inbox/my59.md Green threads
>k9bm.md How to choose a broker?
>4yib.md Investment business is a scam
>4oma.md Message passing
>inbox/er4k.md Mutex
>ref/eg7k.md Null references: the billion dollar mistake
>88el.md Ownership in Rust
>uok6.md Stick to your portfolio strategy
>oumc.md Strings are a complicated data structure
>tdrj.md The Stack and the Heap
>hkvy.md The borrow checker
>wtz9.md Use small Hashable items with diffable data sources
>inbox/dld4.md When to prefer PUT over POST HTTP method?
>zbon.md Zero-cost abstractions in Rust
>18is.md §How to invest in the stock markets?

@ -0,0 +1,18 @@
$ cd new
$ mkdir "a dir"
# Test Handlebars template variables.
$ echo "Piped content" | zk new --group handlebars --title "Note title" --date "January 2nd" --dry-run "a dir"
>id: {{match "[a-z0-9]{4}"}}
>title: Note title
>content: Piped content
>
>dir: a dir
>extra: {"key":"value","visibility":"public"}
>now: 02-01
>env: {{working-dir}}
>filename: note-title.md
>filename-stem: note-title
2>{{working-dir}}/a dir/note-title.md

@ -0,0 +1,103 @@
$ cd new
# Print help for `zk new`
$ zk new --help
>Usage: zk new [<directory>]
>
>Create a new note in the given notebook directory.
>
>Arguments:
> [<directory>] Directory in which to create the note.
>
>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.
>
> -t, --title=TITLE Title of the new note.
> --date=DATE Set the current date.
> -g, --group=NAME Name of the config group this note belongs to.
> Takes precedence over the config of the
> directory.
> --extra=KEY=VALUE,... Extra variables passed to the templates.
> --template=PATH Custom template used to render the note.
> -p, --print-path Print the path of the created note instead of
> editing it.
> -n, --dry-run Don't actually create the note. Instead, prints
> its content on stdout and the generated path on
> stderr.
# Default note title.
$ zk new --print-path
>{{working-dir}}/untitled.md
$ cat untitled.md
># Untitled
>
>
# Provide a custom title.
$ zk new --title "Custom title" --print-path
>{{working-dir}}/custom-title.md
$ cat custom-title.md
># Custom title
>
>
# Provide a custom title (short flag).
$ zk new -t "Another custom title" -p
>{{working-dir}}/another-custom-title.md
# Opens the editor after creating a new note.
$ EDITOR="echo 'edit'" zk new --title "Edit"
>edit {{working-dir}}/edit.md
# Prints the path of newly created note instead of editing it.
$ EDITOR="echo 'edit'" zk new --title "Print path" --print-path
>{{working-dir}}/print-path.md
# Set explicitely today's date.
$ zk new --group date --date "January 2nd" --dry-run
2>{{working-dir}}/02-01.md
$ zk new --group date --date "December 24th" --dry-run
2>{{working-dir}}/24-12.md
# Dry run doesn't write the note.
$ zk new --dry-run --title "Dry run"
># Dry run
>
>
2>{{working-dir}}/dry-run.md
1$ cat dry-run.md
2>cat: dry-run.md: No such file or directory
# Dry run (short flag).
$ zk new -n --title "Dry run"
># Dry run
>
>
2>{{working-dir}}/dry-run.md
# Pipe content in a new note.
$ echo "Content of the note" | EDITOR=cat zk new --title "Piped note"
># Piped note
>
>Content of the note
>
# Existing notes are not overwritten, but can be edited.
$ zk new --force-input n --title "Piped note"
>? piped-note.md already exists, do you want to edit this note instead? (y/N)
# Check that the content was not overwritten
$ cat piped-note.md
># Piped note
>
>Content of the note
>

@ -0,0 +1,174 @@
$ cd tags
# Default format is `full`.
$ zk tag list
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Full format (+long flag).
$ zk tag list --format full
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Name format.
$ zk tag list -fname
>biography
>biology
>book
>dystopia
>feminism
>fiction
>history
>non-fiction
>philosophy
>physics
>romance
>science
>science-fiction
2>
2>Found 13 tags
# JSON format.
$ zk tag list -fjson
>[{"id":7,"kind":"tag","name":"biography","noteCount":1},{"id":8,"kind":"tag","name":"biology","noteCount":2},{"id":14,"kind":"tag","name":"book","noteCount":12},{"id":13,"kind":"tag","name":"dystopia","noteCount":2},{"id":9,"kind":"tag","name":"feminism","noteCount":1},{"id":10,"kind":"tag","name":"fiction","noteCount":6},{"id":6,"kind":"tag","name":"history","noteCount":2},{"id":2,"kind":"tag","name":"non-fiction","noteCount":6},{"id":5,"kind":"tag","name":"philosophy","noteCount":3},{"id":3,"kind":"tag","name":"physics","noteCount":1},{"id":11,"kind":"tag","name":"romance","noteCount":3},{"id":1,"kind":"tag","name":"science","noteCount":3},{"id":12,"kind":"tag","name":"science-fiction","noteCount":1}]
2>
2>Found 13 tags
# JSON Lines format.
$ zk tag list -fjsonl
>{"id":7,"kind":"tag","name":"biography","noteCount":1}
>{"id":8,"kind":"tag","name":"biology","noteCount":2}
>{"id":14,"kind":"tag","name":"book","noteCount":12}
>{"id":13,"kind":"tag","name":"dystopia","noteCount":2}
>{"id":9,"kind":"tag","name":"feminism","noteCount":1}
>{"id":10,"kind":"tag","name":"fiction","noteCount":6}
>{"id":6,"kind":"tag","name":"history","noteCount":2}
>{"id":2,"kind":"tag","name":"non-fiction","noteCount":6}
>{"id":5,"kind":"tag","name":"philosophy","noteCount":3}
>{"id":3,"kind":"tag","name":"physics","noteCount":1}
>{"id":11,"kind":"tag","name":"romance","noteCount":3}
>{"id":1,"kind":"tag","name":"science","noteCount":3}
>{"id":12,"kind":"tag","name":"science-fiction","noteCount":1}
2>
2>Found 13 tags
# Custom format.
$ zk tag list -f"name: \{{name}}, count: \{{note-count}}, kind: \{{kind}}"
>name: biography, count: 1, kind: tag
>name: biology, count: 2, kind: tag
>name: book, count: 12, kind: tag
>name: dystopia, count: 2, kind: tag
>name: feminism, count: 1, kind: tag
>name: fiction, count: 6, kind: tag
>name: history, count: 2, kind: tag
>name: non-fiction, count: 6, kind: tag
>name: philosophy, count: 3, kind: tag
>name: physics, count: 1, kind: tag
>name: romance, count: 3, kind: tag
>name: science, count: 3, kind: tag
>name: science-fiction, count: 1, kind: tag
2>
2>Found 13 tags
# Header.
$ zk tag list -qfname --header "HEADER"
>HEADERbiography
>biology
>book
>dystopia
>feminism
>fiction
>history
>non-fiction
>philosophy
>physics
>romance
>science
>science-fiction
# Footer.
$ zk tag list -qfname --footer "FOOTER\n"
>biography
>biology
>book
>dystopia
>feminism
>fiction
>history
>non-fiction
>philosophy
>physics
>romance
>science
>science-fictionFOOTER
# Delimiter.
$ zk tag list -qfname --delimiter ";"
>biography;biology;book;dystopia;feminism;fiction;history;non-fiction;philosophy;physics;romance;science;science-fiction
# Delimiter (short flag).
$ zk tag list -qfname -d,
>biography,biology,book,dystopia,feminism,fiction,history,non-fiction,philosophy,physics,romance,science,science-fiction
# Can't mix --delimiter0 and --delimiter
1$ zk tag list --delimiter0 --delimiter ","
2>zk: error: --delimiter and --delimiter0 can't be used together
# Can't mix --delimiter0 and --header
1$ zk tag list --delimiter0 --header "-"
2>zk: error: --footer and --delimiter0 can't be used together
# Can't mix --delimiter0 and --footer
1$ zk tag list --delimiter0 --footer "-"
2>zk: error: --footer and --delimiter0 can't be used together
# Can't mix --format json and --header
1$ zk tag list --format json --header "-"
2>zk: error: --header can't be used with JSON format
# Can't mix --format json and --footer
1$ zk tag list --format json --footer "-"
2>zk: error: --footer can't be used with JSON format
# Can't mix --format json and --delimiter
1$ zk tag list --format json --delimiter "-"
2>zk: error: --delimiter can't be used with JSON format
# Can't mix --format jsonl and --header
1$ zk tag list --format jsonl --header "-"
2>zk: error: --header can't be used with JSON format
# Can't mix --format jsonl and --footer
1$ zk tag list --format jsonl --footer "-"
2>zk: error: --footer can't be used with JSON format
# Can't mix --format jsonl and --delimiter
1$ zk tag list --format jsonl --delimiter "-"
2>zk: error: --delimiter can't be used with JSON format

@ -0,0 +1,151 @@
$ cd tags
# Default sort order is by name.
$ zk tag list
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Sort by name ascending (+ long flag).
$ zk tag list --sort name
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Sort by name descending.
$ zk tag list -sname-
>science-fiction (1)
>science (3)
>romance (3)
>physics (1)
>philosophy (3)
>non-fiction (6)
>history (2)
>fiction (6)
>feminism (1)
>dystopia (2)
>book (12)
>biology (2)
>biography (1)
2>
2>Found 13 tags
# Sort by note count ascending.
$ zk tag list -snote-count+
>biography (1)
>feminism (1)
>physics (1)
>science-fiction (1)
>biology (2)
>dystopia (2)
>history (2)
>philosophy (3)
>romance (3)
>science (3)
>fiction (6)
>non-fiction (6)
>book (12)
2>
2>Found 13 tags
# Sort by note count descending.
$ zk tag list -snote-count-
>book (12)
>fiction (6)
>non-fiction (6)
>philosophy (3)
>romance (3)
>science (3)
>biology (2)
>dystopia (2)
>history (2)
>biography (1)
>feminism (1)
>physics (1)
>science-fiction (1)
2>
2>Found 13 tags
# Name shortcut.
$ zk tag list -sn
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Note count shortcut.
$ zk tag list -snc
>book (12)
>fiction (6)
>non-fiction (6)
>philosophy (3)
>romance (3)
>science (3)
>biology (2)
>dystopia (2)
>history (2)
>biography (1)
>feminism (1)
>physics (1)
>science-fiction (1)
2>
2>Found 13 tags
# Sort by several terms.
$ zk tag list -sn-,nc
>book (12)
>non-fiction (6)
>fiction (6)
>science (3)
>romance (3)
>philosophy (3)
>history (2)
>dystopia (2)
>biology (2)
>science-fiction (1)
>physics (1)
>feminism (1)
>biography (1)
2>
2>Found 13 tags
# Unknown sort order.
1$ zk tag list -sfoobar
2>zk: error: foobar: unknown sorting term
2> try name or note-count

@ -0,0 +1,116 @@
$ cd tags
# Print help for `zk tag list`
$ zk tag list --help
>Usage: zk tag list
>
>List all the note tags.
>
>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.
>
>Formatting
> -f, --format=TEMPLATE Pretty print the list using a custom template or one
> of the predefined formats: name, 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.
>
>Sorting
> -s, --sort=TERM,... Order the tags by the given criterion.
# List all tags.
$ zk tag list
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags
# Quiet mode.
$ zk tag list --quiet
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
# Quiet mode (short).
$ zk tag list -q
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
# Remove some tags.
$ rm the*
$ zk tag list
>book (6)
>dystopia (2)
>fiction (5)
>non-fiction (1)
>physics (1)
>romance (2)
>science (1)
>science-fiction (1)
2>
2>Found 8 tags
# Add new tags.
$ echo "#tag1 #tag2 #science" > ulysses.md
$ zk tag list
>book (5)
>dystopia (2)
>fiction (4)
>non-fiction (1)
>physics (1)
>romance (2)
>science (2)
>science-fiction (1)
>tag1 (1)
>tag2 (1)
2>
2>Found 10 tags
# Remove all tags.
$ rm *
$ zk tag list
2>
2>Found 0 tag

@ -0,0 +1,37 @@
$ cd tags
# Print help for `zk tag`.
$ zk tag --help
>Usage: zk tag <command>
>
>Manage the note tags.
>
>Commands:
> tag list List all the note tags.
>
>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.
# The default command is `tag list`.
$ zk tag
>biography (1)
>biology (2)
>book (12)
>dystopia (2)
>feminism (1)
>fiction (6)
>history (2)
>non-fiction (6)
>philosophy (3)
>physics (1)
>romance (3)
>science (3)
>science-fiction (1)
2>
2>Found 13 tags

@ -0,0 +1,38 @@
$ cd blank
# Setup note fixtures.
$ mkdir "red planet"
$ touch "without-title.md"
$ echo "# Yellow sun" > "yellow-sun.md"
$ touch "red planet/blue moon.md"
# Alias to override the default flags of a command.
$ echo "[alias] list = 'zk list --quiet -fpath \$@'" > .zk/config.toml
$ zk list -n2 --sort path-
>yellow-sun.md
>without-title.md
# Shortcut for a native command.
$ echo "[alias] ls = 'zk list \$@'" > .zk/config.toml
$ zk ls --quiet -fpath
>red planet/blue moon.md
>without-title.md
>yellow-sun.md
# Use $*
$ echo "[note] filename = '\{{slug title}}'\n [alias] nt = 'zk new --dry-run --title \"\$*\"'" > .zk/config.toml
$ zk nt Hello world
2>{{working-dir}}/hello-world.md
# Use the `ZK_NOTEBOOK_DIR` env variable.
$ echo "[alias] nbdir = 'echo \$ZK_NOTEBOOK_DIR'" > .zk/config.toml
$ zk nbdir
>{{working-dir}}
# Test the "xargs formula"
$ echo "[alias] xargs = 'zk list --quiet --format path --delimiter0 \$@ | xargs -0 ls -s1'" > .zk/config.toml
$ zk xargs
>0 red planet/blue moon.md
>0 without-title.md
>8 yellow-sun.md

@ -0,0 +1,30 @@
$ cd extra
# Test usage of `extra` in templates.
$ zk new --dry-run --title "Test"
># Test
>
>Visibility: public
>Color: red
>Extra: {"color":"red","visibility":"public"}
2>{{working-dir}}/public-test.md
# Overridden extra in groups.
$ zk new journal --dry-run --title "Test"
># Test
>
>Visibility: private
>Color: red
>Extra: {"color":"red","visibility":"private"}
2>{{working-dir}}/journal/private-test.md
# Overridden extra on the CLI.
$ zk new journal --dry-run --title "Test" --extra visibility=protected,show-header=1
># Test
>Behold, the mighty dynamic header!
>
>Visibility: protected
>Color: red
>Extra: {"color":"red","show-header":"1","visibility":"protected"}
2>{{working-dir}}/journal/protected-test.md

@ -0,0 +1,27 @@
$ cd blank
$ touch banana.md
$ touch orange.md
$ mkdir dir
$ touch dir/orange.md
$ touch dir/apple.md
$ touch dir/pear.md
# Use a custom filter with `zk list`.
$ echo "[filter] recents = '--sort created- --limit 2'" > .zk/config.toml
$ zk list -qfpath recents
>dir/pear.md
>dir/apple.md
# Filter named after a directory to override the default filtering options.
$ echo "[filter] dir = 'dir --sort path-'" > .zk/config.toml
$ zk list -qfpath dir
>dir/pear.md
>dir/orange.md
>dir/apple.md
# Nested filters.
$ echo "nested = 'dir --limit 2'" >> .zk/config.toml
$ zk list -qfpath nested
>dir/pear.md
>dir/orange.md

@ -0,0 +1,62 @@
$ cd blank
# Setup note fixtures.
$ mkdir "red planet"
$ touch "without-title.md"
$ echo "---\ncolor: yellow\n---\n# Yellow sun" > "yellow-sun.md"
$ echo "# Blue moon" > "red planet/blue moon.md"
# Default link format is `markdown`, without extension.
$ zk list -qflink
>[](without-title)
>[Blue moon](red%20planet/blue%20moon)
>[Yellow sun](yellow-sun)
# Use `wiki` link format.
$ echo "[format.markdown] link-format = 'wiki'" > .zk/config.toml
$ zk list -qflink
>[[without-title]]
>[[red planet/blue moon]]
>[[yellow-sun]]
# Use a custom link format.
# {{json .}} will print the whole template context.
$ echo "[format.markdown] link-format = '\{{json .}}'" > .zk/config.toml
$ zk list -qflink
>{"filename":"without-title","path":"without-title","absPath":"{{working-dir}}/without-title","relPath":"without-title","title":"","metadata":{}}
>{"filename":"blue moon","path":"red planet/blue moon","absPath":"{{working-dir}}/red planet/blue moon","relPath":"red planet/blue moon","title":"Blue moon","metadata":{}}
>{"filename":"yellow-sun","path":"yellow-sun","absPath":"{{working-dir}}/yellow-sun","relPath":"yellow-sun","title":"Yellow sun","metadata":{"color":"yellow"}}
# Paths are relative to the current directory.
$ zk list -qflink -W "red planet"
>{"filename":"without-title","path":"without-title","absPath":"{{working-dir}}/without-title","relPath":"../without-title","title":"","metadata":{}}
>{"filename":"blue moon","path":"red planet/blue moon","absPath":"{{working-dir}}/red planet/blue moon","relPath":"blue moon","title":"Blue moon","metadata":{}}
>{"filename":"yellow-sun","path":"yellow-sun","absPath":"{{working-dir}}/yellow-sun","relPath":"../yellow-sun","title":"Yellow sun","metadata":{"color":"yellow"}}
# Don't drop the extension.
$ echo "link-drop-extension = false" >> .zk/config.toml
$ zk list -qflink
>{"filename":"without-title.md","path":"without-title.md","absPath":"{{working-dir}}/without-title.md","relPath":"without-title.md","title":"","metadata":{}}
>{"filename":"blue moon.md","path":"red planet/blue moon.md","absPath":"{{working-dir}}/red planet/blue moon.md","relPath":"red planet/blue moon.md","title":"Blue moon","metadata":{}}
>{"filename":"yellow-sun.md","path":"yellow-sun.md","absPath":"{{working-dir}}/yellow-sun.md","relPath":"yellow-sun.md","title":"Yellow sun","metadata":{"color":"yellow"}}
# Encode paths.
$ echo "link-encode-path = true" >> .zk/config.toml
$ zk list -qflink
>{"filename":"without-title.md","path":"without-title.md","absPath":"{{working-dir}}/without-title.md","relPath":"without-title.md","title":"","metadata":{}}
>{"filename":"blue%20moon.md","path":"red%20planet/blue%20moon.md","absPath":"{{working-dir}}/red%20planet/blue%20moon.md","relPath":"red%20planet/blue%20moon.md","title":"Blue moon","metadata":{}}
>{"filename":"yellow-sun.md","path":"yellow-sun.md","absPath":"{{working-dir}}/yellow-sun.md","relPath":"yellow-sun.md","title":"Yellow sun","metadata":{"color":"yellow"}}
# Test individual template variables.
$ echo "[format.markdown] link-format = '\{{filename}} \{{title}} \{{json metadata}}'" > .zk/config.toml
$ zk list -qflink
>without-title {}
>blue moon Blue moon {}
>yellow-sun Yellow sun {"color":"yellow"}
$ echo "[format.markdown] link-format = '\{{path}} \{{rel-path}} \{{abs-path}}'" > .zk/config.toml
$ zk list -qflink -W red\ planet
>without-title ../without-title {{working-dir}}/without-title
>red planet/blue moon blue moon {{working-dir}}/red planet/blue moon
>yellow-sun ../yellow-sun {{working-dir}}/yellow-sun

@ -0,0 +1,43 @@
$ cd blank
$ echo "#hashtag\n#multi-word tag#\n:colon-tag:" > note.md
# By default, only hashtags are enabled.
$ echo "" > .zk/config.toml
$ zk tag list
>hashtag (1)
>multi-word (1)
2>
2>Found 2 tags
# Disable hashtags.
$ echo "[format.markdown] hashtags = false" > .zk/config.toml
$ zk index -fq
$ zk tag list
2>
2>Found 0 tag
# Enable colon tags.
$ echo "[format.markdown] hashtags = false\n colon-tags = true" > .zk/config.toml
$ zk index -fq
$ zk tag list
>colon-tag (1)
2>
2>Found 1 tag
# Enable Bear multi-word tags.
$ echo "[format.markdown] multiword-tags = true" > .zk/config.toml
$ zk index -fq
$ zk tag list
>hashtag (1)
>multi-word tag (1)
2>
2>Found 2 tags
# Bear multi-word tags require hashtags to be enabled
$ echo "[format.markdown] hashtags = false\n multiword-tags = true" > .zk/config.toml
$ zk index -fq
$ zk tag list
2>
2>Found 0 tag

@ -0,0 +1,29 @@
$ cd group-paths
# No group.
$ zk new --title "Orange" --dry-run
2>{{working-dir}}/orange.md
# Implicit group.
$ zk new implicit --title "Green" --dry-run
2>{{working-dir}}/implicit/implicit-green.md
# Implicit group in subdirectory.
$ zk new dir/implicit\ subdir --title "Blue" --dry-run
2>{{working-dir}}/dir/implicit subdir/implicit-subdir-blue.md
# Explicit paths.
$ zk new daily --title "Red" --dry-run
2>{{working-dir}}/daily/journal-red.md
$ zk new weekly --title "Yellow" --dry-run
2>{{working-dir}}/weekly/journal-yellow.md
# Explicit paths with globs.
$ zk new journal/a --title "Black" --dry-run
2>{{working-dir}}/journal/a/journal-black.md
$ zk new journal/b --title "White" --dry-run
2>{{working-dir}}/journal/b/journal-white.md
# Won't work in the root.
$ zk new journal --title "Purple" --dry-run
2>{{working-dir}}/journal/purple.md

@ -0,0 +1,23 @@
$ cd group
# Default group.
$ zk new --title "Red" --dry-run
># Red
>
>Default content
2>{{working-dir}}/red.md
# Group associated with a path.
$ zk new journal --title "Green" --date "January 2nd" --dry-run
># Journal: Green
>
>What did you do today?
2>{{working-dir}}/journal/02-01.md
# Overridden group from the CLI.
$ zk new --group journal --title "Blue" --date "January 2nd" --dry-run
># Journal: Blue
>
>What did you do today?
2>{{working-dir}}/02-01.md

@ -0,0 +1,13 @@
$ cd blank
$ echo "[note]\n filename = '\{{title}}'" > .zk/config.toml
# The default title is Untitled.
$ zk new --dry-run
2>{{working-dir}}/Untitled.md
# Set a custom default title.
$ echo "default-title = 'Sans titre'" >> .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/Sans titre.md

@ -0,0 +1,26 @@
$ cd blank
# The default filename is a random ID of 4 lowercase alphanumeric characters, with .md extension.
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z0-9]{4}"}}.md
# Set a custom filename.
$ echo "[note]\n filename = '\{{slug title}} - \{{date now \"%m-%d\"}}'" > .zk/config.toml
$ zk new --title "A new note" --date "January 5th" --dry-run
2>{{working-dir}}/a-new-note - 01-05.md
# Set a custom extension.
$ echo "extension = 'markdown'" >> .zk/config.toml
$ zk new --title "A new note" --date "January 5th" --dry-run
2>{{working-dir}}/a-new-note - 01-05.markdown
# Test the filename Handlebars variables.
$ mkdir "a dir"
$ echo "[note]\n filename = '\{{title}},\{{content}},\{{date now \"%m-%d\"}},\{{json extra}}'" > .zk/config.toml
$ echo "Piped content" | zk new --title "A new note" --date "January 5th" --extra key=value --dry-run
2>{{working-dir}}/A new note,Piped content
2>,01-05,{"key":"value"}.md
$ echo "[note]\n filename = '\{{id}},\{{dir}},\{{json extra}},\{{env.ZK_NOTEBOOK_DIR}}'" > .zk/config.toml
$ echo "Piped content" | zk new --title "A new note" --date "January 5th" --dry-run "a dir"
2>{{working-dir}}/a dir/{{match "[a-z0-9]{4}"}},a dir,{},{{working-dir}}.md

@ -0,0 +1,50 @@
$ cd blank
# The default ID generation uses 4 lowercase alphanumeric characters.
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z0-9]{4}"}}.md
# Custom ID length.
$ echo "[note] id-length = 100" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z0-9]{100}"}}.md
# Uppercase.
$ echo "[note] id-length = 100\n id-case = 'upper'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[A-Z0-9]{100}"}}.md
# Lowercase.
$ echo "[note] id-length = 100\n id-case = 'lower'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z0-9]{100}"}}.md
# Mixed case.
$ echo "[note] id-length = 100\n id-case = 'mixed'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-zA-Z0-9]{100}"}}.md
# Letters charset.
$ echo "[note] id-length = 100\n id-charset = 'letters'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z]{100}"}}.md
# Numbers charset.
$ echo "[note] id-length = 100\n id-charset = 'numbers'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[0-9]{100}"}}.md
# Alphanumeric charset.
$ echo "[note] id-length = 100\n id-charset = 'alphanum'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-z0-9]{100}"}}.md
# Hexadecimal charset.
$ echo "[note] id-length = 100\n id-charset = 'hex'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-f0-9]{100}"}}.md
# Custom charset.
$ echo "[note] id-length = 100\n id-charset = 'abc01'" > .zk/config.toml
$ zk new --dry-run
2>{{working-dir}}/{{match "[a-c01]{100}"}}.md

@ -0,0 +1,49 @@
$ cd blank
$ mkdir dir
$ mkdir dir/subdir
$ touch banana.md
$ touch orange.md
$ touch dir/orange.md
$ touch dir/subdir/apple.md
# Ignore a file in the root directory.
$ echo "[note]\n ignore = ['orange.md']" > .zk/config.toml
$ zk index -v
>- added banana.md
>- added dir/orange.md
>- added dir/subdir/apple.md
>- ignored orange.md: matched ignore glob "orange.md"
>
>Indexed 3 notes in 0s
> + 3 added
> ~ 0 modified
> - 0 removed
# Ignore with wildcards.
$ echo "[note]\n ignore = ['*rang*', 'dir/*']" > .zk/config.toml
$ zk index -v
>- unchanged banana.md
>- removed dir/orange.md
>- unchanged dir/subdir/apple.md
>- ignored dir/orange.md: matched ignore glob "dir/*"
>- ignored orange.md: matched ignore glob "*rang*"
>
>Indexed 2 notes in 0s
> + 0 added
> ~ 0 modified
> - 1 removed
# Unignore all files.
$ echo "" > .zk/config.toml
$ zk index -v
>- unchanged banana.md
>- added dir/orange.md
>- unchanged dir/subdir/apple.md
>- added orange.md
>
>Indexed 4 notes in 0s
> + 2 added
> ~ 0 modified
> - 0 removed

@ -0,0 +1,15 @@
$ cd blank
$ echo "[note]\n filename = '\{{slug title}} - \{{date now \"%B\"}}'" > .zk/config.toml
# The default language is `en`.
# Note the & converted to `and` in the slug.
$ zk new --title "Foo \& Bar" --date "January 2nd" --dry-run
2>{{working-dir}}/foo-and-bar - January.md
# Set a custom language.
# Note the & converted to `et` in the slug.
$ echo "language = 'fr'" >> .zk/config.toml
$ zk new --title "Ceci \& cela" --date "January 2nd" --dry-run
2>{{working-dir}}/ceci-et-cela - January.md

@ -0,0 +1,18 @@
$ cd blank
$ mkdir .zk/templates
$ echo "# \{{title}}" > .zk/templates/custom.md
# Set a custom template.
$ echo "[note] filename = '\{{slug title}}'\n template = 'custom.md'" > .zk/config.toml
$ zk new --title "A new note" --dry-run
># A new note
2>{{working-dir}}/a-new-note.md
# Template not found.
$ echo "[note] template = 'not-found'" > .zk/config.toml
1$ zk new --dry-run
2>zk: error: new note: load template file failed: cannot find template at not-found

@ -0,0 +1,3 @@
# Blue
Content of blue

@ -0,0 +1,3 @@
# Green
Content of green

@ -0,0 +1,3 @@
# Purple
Content of purple

@ -0,0 +1,3 @@
# Red
Content of red

@ -0,0 +1,3 @@
# Yellow
Content of yellow

@ -0,0 +1,10 @@
[note]
template = "default.md"
filename = "{{extra.visibility}}-{{slug title}}"
[extra]
color = "red"
visibility = "public"
[group.journal.extra]
visibility = "private" # overrides

@ -0,0 +1,8 @@
# {{title}}
{{#if extra.show-header}}
Behold, the mighty dynamic header!
{{/if}}
Visibility: {{extra.visibility}}
Color: {{extra.color}}
Extra: {{json extra}}

@ -0,0 +1,2 @@
[format.markdown]
colon-tags = true

@ -0,0 +1,3 @@
# {{title}}
{{content}}

@ -0,0 +1,20 @@
# §How to invest in the stock markets?
## How the markets work
* [Financial markets are random](fa2k)
* [Compound interests make you rich](smdc)
* [The less you think about the market, the more money you make](uok6)
* [Investment business is a scam](4yib)
## Good rules to follow
* [Diversify your portfolio](aqfd)
* [Buy low, sell high](uxjt)
* [Don't speculate](pywo)
## How to invest
* [How to choose a broker?](k9bm)
:finance:

@ -0,0 +1,9 @@
# Fearless concurrency
* A term coined by the Rust team to advocate the [ownership pattern](88el) as a way to not only solve memory bugs but also most concurrency bugs at compile time.
## Reference
* [Fearless Concurrency - The Rust Programming Language](https://doc.rust-lang.org/book/ch16-00-concurrency.html)
:programming:

@ -0,0 +1,13 @@
# Data race error
A *data race* is similar to a race condition and happens when:
* Two or more pointers access the same data at the same time.
* At least one of the pointers is being used to write to the data.
* There's no synchronization mechanism to protect the data.
*Data races* cause undefined behavior and are hard to debug.
Rust prevents *data races* by allowing only a single mutable reference of a value per scope.
:programming:

@ -0,0 +1,11 @@
---
aliases: [dangling reference]
---
# Dangling pointers
A *dangling pointer* is a reference that is kept to freed data. With C, reading it causes a *segmentation fault*.
Rust protects against *dangling pointers* by making sure data is not freed until it goes out of scope ([Ownership in Rust](88el)).
:programming:

@ -0,0 +1,7 @@
# Message passing
* A popular approach for safe concurrency is to use *message passing* instead of shared state.
* Channels are an example of a message passing implementation.
* The Go language is advocating for this approach with their slogan: "[Do not communicate by sharing memory; instead, share memory by communicating](ref/7fto)".
:programming:

@ -0,0 +1,22 @@
# Investment business is a scam
## Don't trust financial advisers
Don't trust anyone selling financial products, even if they are endorsed by your employer and especially if:
- the fees are a recurring % of the portfolio ([Compound interests make you rich](smdc))
- they are offering gifts or holidays
- urgent one-time offers needing immediate decision
They are trying to exploit your emotions caused by the fact that [financial markets are random](fa2k).
## Avoid managed financial products
Avoid at all cost actively managed fund, because:
- the recurring fees will eat your interests
- they attract more taxes
Avoid QROPS (offshore pensions) for expats.
:finance:

@ -0,0 +1,7 @@
# Ownership in Rust
Ownership is one of Rust's most unique features and replaces efficiently a GC for memory management.
The Ownership is a method to manage data on the Heap ([The Stack and the Heap](tdrj)).
:programming:rust:

@ -0,0 +1,13 @@
# Diversify your portfolio
Diversifying a portfolio makes sure that its value is not too volatile, because [financial markets are random](fa2k).
Buy funds representing the market as a whole (industry sectors, regions, etc.) instead of individual stocks, remembering to [buy when the prices are low](uxjt).
Your portfolio should be a combination of:
- A global stock market index fund to provide global exposure.
- A domestic stock index fund to represent your homecountry stock market (or the country where you plan to retire).
- A domestic bond market index fund to stabilize the value of the portfolio.
:finance:

@ -0,0 +1,5 @@
# Financial markets are random
The markets' value is highly volatile on short term, but has a steady long term growth (> 10 years). Your best bet is to [stick to your portfolio strategy](uok6) and trust that [compound interests will make you rich](smdc).
:finance:

@ -0,0 +1,6 @@
# Channel
* Channels are a great approach for safe concurrency.
* It's an implementation of the [message passing](4oma) pattern.
:programming:

@ -0,0 +1,12 @@
# Concurrency in Rust
* Thanks to the [Ownership pattern](88el), Rust has a model of [Fearless concurrency](2cl7).
* Rust aims to have a small runtime, so it doesn't support [green threads](inbox/my59).
* Crates exist to add support for green threads if needed.
* Instead, Rust relies on the OS threads, a model called 1-1.
* Rust offers a number of constructs for sharing data between threads:
* [Channel](fwsj) for a safe [message passing](4oma) approach.
* [Mutex](inbox/er4k) for managing shared state.
:rust:programming:

@ -0,0 +1,7 @@
# The borrow checker
The *borrow checker* of Rust's compiler is comparing the scope of borrowed references to the scope of the owned data to prevent dangling references. It also makes sure that the relationship between *lifetimes* of several reference match.
In some deterministic patterns, the *borrow checker* automatically infer the lifetimes following [lifetime elision rules](t9i4). But when the *borrow checker* can't automatically infer the lifetimes, we need to help it by annotating our references with [generic lifetime annotations](554k).
:programming:rust:

@ -0,0 +1,12 @@
# Errors should be handled differently in an application versus a library
Error handling should be approached differently depending on the context.
* A *library* should focus on *producing* errors with meaningful context, by wrapping lower-level errors.
* An *application* mainly consumes errors, by deciding how they are formatted and presented to the user.
## References
* [Rust: Structuring and handling errors in 2020 - nick.groenen.me](https://nick.groenen.me/posts/rust-error-handling/)
:programming:

@ -0,0 +1,14 @@
---
date: 2011-05-16 09:58:57
keywords: [programming, http]
category: "Best practice"
---
# When to prefer PUT over POST HTTP method?
`PUT` should be idempotent. This means that it's harmless to call a `PUT` request many times. On the contrary, calling `POST` requests repeatedly might change data on the server again.
A way to see it is:
* `PUT` = SQL `UPDATE`
* `POST` = SQL `INSERT`

@ -0,0 +1,10 @@
# Mutex
* Abbreviation of *mutual exclusion*.
* An approach to manage safely shared state by allowing only a single thread to access a protected value at one time.
* A mutex *guards* a protected data with a *locking system*.
* Managing mutexes is tricky, using [channels](../fwsj) is an easier alternative.
* The main risk is to create *deadlocks*.
* Thanks to its [Ownership](../88el) pattern, Rust makes sure we can't mess up when using locks.
:programming:

@ -0,0 +1,8 @@
# Green threads
> Many operating systems provide an API for creating new threads. This model where a language calls the operating system APIs to create threads is sometimes called 1:1, meaning one operating system thread per one language thread.
> Many programming languages provide their own special implementation of threads. Programming language-provided threads are known as green threads, and languages that use these green threads will execute them in the context of a different number of operating system threads. For this reason, the green-threaded model is called the M:N model: there are M green threads per N operating system threads, where M and N are not necessarily the same number
> [Using Threads to Run Code Simultaneously - The Rust Programming Language](https://doc.rust-lang.org/book/ch16-01-threads.html)
:programming:

@ -0,0 +1,13 @@
# How to choose a broker?
A broker will trade your portfolio for you on the markets.
You should choose a broker that is located in a country where the government is stable and where banking regulations are solid.
Don't trade more than once a month, since brokers charge commissions when you trade.
On-going fees as percentage of a portfolio's assets are the real killer ([Investment business is a scam](4yib)) because [compound interests make you rich](smdc). However, commission fees on trading are okay.
It might be worth choosing a broker in your country, for easier tax filling.
:finance:

@ -0,0 +1,14 @@
# Strings are a complicated data structure
Given the Hindi word "नमस्ते":
1. It can be represented as a byte array of 18 bytes:
`[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135]`
2. If you look at Unicode scalar values, you get an array of 6 characters:
`['न', 'म', 'स', '्', 'त', 'े']`
3. But the fourth and sixth letters are diacritics. To get the human-readable letters, you need to look at the strings as an array of *grapheme clusters*:
`["न", "म", "स्", "ते"]`
:programming:

@ -0,0 +1,9 @@
# Don't speculate
You have more to loose by doing market timing (jumping in and out of the stocks market). Remember, [the less you think about the market, the more money you make](uok6).
For example, from 1982 to 2005, the market averaged 10.6 percent annually, but if you missed the 10 best trading days, your return would drop to 8.1 percent, or 1.8 percent for the 50 best trading days.
Take your money out of the stock market for a day, a week or a month and you could miss the best trading days of the decade, nobody can predict those because [financial markets are random](fa2k).
:finance:

@ -0,0 +1,6 @@
# Do not communicate by sharing memory; instead, share memory by communicating
* Advocates for the use of [message passing](4oma) instead of shared state.
* A slogan initially coined by Rob Pike ([Effective Go - Concurrency](https://golang.org/doc/effective_go.html#concurrency)).
:programming:

@ -0,0 +1,7 @@
# Null references: the billion dollar mistake
Tony Hoare, the inventor of `null`, has this to say:
> I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
:programming:

@ -0,0 +1,21 @@
# Compound interests make you rich
Since the growth is exponential, time is more important than the amount of money you invest with compound interests. Start investing right now!
This also means that small interest percentages add up to big amount. So [beware of financial products](4yib) eating your interests.
Buy new shares with the interests to benefit from the compound interests, e.g. after a unique investment of $1,000 with a 10% interest rate:
- without reinvesting the dividends:
- 40 yrs = $5,000
- 50 yrs = $6,000
- with compound interest:
- 40 yrs = $45,000
- 50 yrs = $117,000
## References
- [These 3 Charts Show The Amazing Power Of Compound Interest](https://www.businessinsider.com/personal-finance/amazing-power-of-compound-interest-2014-7?r=DE&IR=T)
:finance:

@ -0,0 +1,27 @@
# The Stack and the Heap
Both the Stack and the Heap are parts of the memory that is accessible to an app during runtime.
## Stack
* The Stack stores values in a last in, first out fashion.
* All values stored must have a known fixed size.
* Data with unknown or changeable size must be stored on the Heap instead.
* Are stored on the Stack:
* Arguments and local variables when calling a function.
## Heap
* The Heap is less organized than the Stack
* When *allocating on the Heap*, the memory allocator:
1. Looks for an empty spot for the requested size.
2. Marks the spot as reserved.
3. Returns a pointer to the spot.
* The pointer is then usually stored on the Stack. When we want to access the data, we must follow the pointer to the Heap.
## Stack vs Heap
* *Pushing to the Stack* is faster than *allocating on the Heap* because the Stack doesn't need to find an empty spot.
* Accessing data from the Stack is also faster because we don't have to jump around in the memory and to follow pointers.
:programming:

@ -0,0 +1,13 @@
# Stick to your portfolio strategy
Choose a portfolio strategy, such as the [Couch potato investment strategy](hdi6), and stick to it.
Second-guessing and tweaking make your portfolio under perform. Ignore your emotions when the market fluctuates, this derails most investors. Instead, keep steady and trust that [compound interests make you rich](smdc). A good way to do that is to look at your investments performance only once a year.
## The less you think about the market, the more money you make
Don't listen to investment news, stock predictions or economic forecasts.
Don't tweak your investments to adjust to world events, there's not always a logical connection between world events and the markets performance, because the [financial markets are random](fa2k).
:finance:

@ -0,0 +1,9 @@
# Buy low, sell high
It's better to invest when the prices are low, because it will usually go up on the long term, despite the fact that [financial markets are random](fa2k).
Don't wait until you think the stocks are at their lowest ([speculation](pywo)), instead buy some when the prices are dropping, and buy more every month if the prices continue to drop.
Investing a constant amount of money regularly (e.g. monthly) is a simple way to make sure you buy less stocks when the prices are high, and more when they are low. [Compound interests will work for you over time](smdc).
:finance:

@ -0,0 +1,5 @@
# Use small `Hashable` items with diffable data sources
If `apply()` is too slow with a diffable data source, it's probably because the items take too long to be hashed. A best practice is to hash only the properties that are actually used for display in the view.
:programming:swift:ios:

@ -0,0 +1,9 @@
# Zero-cost abstractions in Rust
*Zero-cost abstractions* is a term touted (coined?) by Rust-enthusiasts. It means that using higher-level features such as `map`, `filter`, `iterators`, etc. has no performance downsides. They get compiled to the same code we would write by hand with a `for` loop.
## Reference
* [Comparing Performance: Loops vs. Iterators - The Rust Programming Language](https://doc.rust-lang.org/book/ch13-04-performance.html)
:programming:rust:

@ -0,0 +1,21 @@
[note]
filename = "{{slug title}}"
# Path derived from the group name.
[group.implicit.note]
filename = "implicit-{{slug title}}"
# Path derived from the group name, in a sub-directory.
[group."dir/implicit subdir".note]
filename = "implicit-subdir-{{slug title}}"
# Group with explicit paths.
[group.journal]
paths = [
"daily",
"weekly",
"journal/*"
]
[group.journal.note]
filename = "journal-{{slug title}}"

@ -0,0 +1,13 @@
[note]
filename = "{{slug title}}"
template = "default.md"
[extra]
content = "Default content"
[group.journal.note]
filename = "{{date now '%d-%m'}}"
template = "journal.md"
[group.journal.extra]
content = "What did you do today?"

@ -0,0 +1,3 @@
# {{title}}
{{extra.content}}

@ -0,0 +1,3 @@
# Journal: {{title}}
{{extra.content}}

@ -0,0 +1,4 @@
[note]
ignore = [
"carrot-ignored/*",
]

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save