From 101c12d5d806fedc69c80745fc0dbf943f23b8a2 Mon Sep 17 00:00:00 2001 From: Michael McDonagh Date: Mon, 8 Dec 2025 20:19:35 -0500 Subject: [PATCH 1/5] Switch to any --- internal/adapter/lsp/cmd_new.go | 2 +- internal/cli/cmd/new.go | 20 ++++++++++---------- internal/core/config.go | 12 ++++++------ internal/core/config_test.go | 20 ++++++++++---------- internal/core/note_new.go | 4 ++-- internal/core/note_new_test.go | 26 +++++++++++++------------- internal/core/notebook.go | 2 +- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/internal/adapter/lsp/cmd_new.go b/internal/adapter/lsp/cmd_new.go index f21b83d5..538bafb9 100755 --- a/internal/adapter/lsp/cmd_new.go +++ b/internal/adapter/lsp/cmd_new.go @@ -20,7 +20,7 @@ type cmdNewOpts struct { Dir string `json:"dir"` Group string `json:"group"` Template string `json:"template"` - Extra map[string]string `json:"extra"` + Extra map[string]any `json:"extra"` Date string `json:"date"` Edit jsonBoolean `json:"edit"` DryRun jsonBoolean `json:"dryRun"` diff --git a/internal/cli/cmd/new.go b/internal/cli/cmd/new.go index bc672b81..afffedea 100644 --- a/internal/cli/cmd/new.go +++ b/internal/cli/cmd/new.go @@ -16,16 +16,16 @@ import ( // New adds a new note to the notebook. type New struct { - Directory string `arg optional default:"." help:"Directory in which to create the note."` - Interactive bool `short:i help:"Read contents from standard input."` - 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."` - PrintPath bool `short:p help:"Print the path of the created note instead of editing it."` - DryRun bool `short:n help:"Don't actually create the note. Instead, prints its content on stdout and the generated path on stderr."` - ID string ` placeholder:ID help:"Skip id generation and use provided value."` + Directory string `arg optional default:"." help:"Directory in which to create the note."` + Interactive bool `short:i help:"Read contents from standard input."` + 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]any ` help:"Extra variables passed to the templates." mapsep:","` + Template string ` placeholder:PATH help:"Custom template used to render the note."` + PrintPath bool `short:p help:"Print the path of the created note instead of editing it."` + DryRun bool `short:n help:"Don't actually create the note. Instead, prints its content on stdout and the generated path on stderr."` + ID string ` placeholder:ID help:"Skip id generation and use provided value."` } func (cmd *New) Run(container *cli.Container) error { diff --git a/internal/core/config.go b/internal/core/config.go index 1e5ecec7..27fc5861 100644 --- a/internal/core/config.go +++ b/internal/core/config.go @@ -22,7 +22,7 @@ type Config struct { LSP LSPConfig Filters map[string]string Aliases map[string]string - Extra map[string]string + Extra map[string]any } // NOTE: config generation occurs in core.Init. The below function is used @@ -74,7 +74,7 @@ func NewDefaultConfig() Config { }, Filters: map[string]string{}, Aliases: map[string]string{}, - Extra: map[string]string{}, + Extra: map[string]any{}, } } @@ -237,7 +237,7 @@ type NoteConfig struct { type GroupConfig struct { Paths []string Note NoteConfig - Extra map[string]string + Extra map[string]any } // ExcludeGlobs returns all the Note.Exclude path globs for the group paths, @@ -263,7 +263,7 @@ func (c GroupConfig) Clone() GroupConfig { clone.Paths = make([]string, len(c.Paths)) copy(clone.Paths, c.Paths) - clone.Extra = make(map[string]string) + clone.Extra = map[string]any{} for k, v := range c.Extra { clone.Extra[k] = v } @@ -521,7 +521,7 @@ type tomlConfig struct { Format tomlFormatConfig Tool tomlToolConfig LSP tomlLSPConfig - Extra map[string]string + Extra map[string]any Filters map[string]string `toml:"filter"` Aliases map[string]string `toml:"alias"` } @@ -546,7 +546,7 @@ type tomlNoteConfig struct { type tomlGroupConfig struct { Paths []string Note tomlNoteConfig - Extra map[string]string + Extra map[string]any } type tomlFormatConfig struct { diff --git a/internal/core/config_test.go b/internal/core/config_test.go index 12da2dfc..2ef379c9 100644 --- a/internal/core/config_test.go +++ b/internal/core/config_test.go @@ -56,7 +56,7 @@ func TestParseDefaultConfig(t *testing.T) { }, Filters: make(map[string]string), Aliases: make(map[string]string), - Extra: make(map[string]string), + Extra: make(map[string]any), }) } @@ -180,7 +180,7 @@ func TestParseComplete(t *testing.T) { DefaultTitle: "Ohne Titel", Exclude: []string{"ignored", ".git", "new-ignored"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", "log-ext": "value", @@ -201,7 +201,7 @@ func TestParseComplete(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", }, @@ -221,7 +221,7 @@ func TestParseComplete(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", }, @@ -268,7 +268,7 @@ func TestParseComplete(t *testing.T) { "ls": "zk list $@", "ed": "zk edit $@", }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", }, @@ -386,7 +386,7 @@ func TestParseMergesGroupConfig(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "override", "salut": "le monde", "log-ext": "value", @@ -407,7 +407,7 @@ func TestParseMergesGroupConfig(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", }, @@ -438,7 +438,7 @@ func TestParseMergesGroupConfig(t *testing.T) { }, Filters: make(map[string]string), Aliases: make(map[string]string), - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", "salut": "le monde", }, @@ -607,7 +607,7 @@ func TestGroupConfigClone(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", }, } @@ -644,7 +644,7 @@ func TestGroupConfigClone(t *testing.T) { DefaultTitle: "Sans titre", Exclude: []string{"ignored", ".git"}, }, - Extra: map[string]string{ + Extra: map[string]any{ "hello": "world", }, }) diff --git a/internal/core/note_new.go b/internal/core/note_new.go index 97a3cb44..2dd5c914 100644 --- a/internal/core/note_new.go +++ b/internal/core/note_new.go @@ -13,7 +13,7 @@ type newNoteTask struct { title string content string date time.Time - extra map[string]string + extra map[string]any env map[string]string fs FileStorage filenameTemplate string @@ -104,7 +104,7 @@ type newNoteTemplateContext struct { Dir string Filename string FilenameStem string `handlebars:"filename-stem"` - Extra map[string]string + Extra map[string]any Now time.Time Env map[string]string } diff --git a/internal/core/note_new_test.go b/internal/core/note_new_test.go index 59d0b93a..dda76446 100644 --- a/internal/core/note_new_test.go +++ b/internal/core/note_new_test.go @@ -20,7 +20,7 @@ func TestNotebookNewNote(t *testing.T) { note, err := test.run(NewNoteOpts{ Title: opt.NewString("Note title"), Content: "Note content", - Extra: map[string]string{ + Extra: map[string]any{ "add-extra": "ec83da", }, Date: now, @@ -45,7 +45,7 @@ func TestNotebookNewNote(t *testing.T) { Dir: "", Filename: "", FilenameStem: "", - Extra: map[string]string{"add-extra": "ec83da", "conf-extra": "38srnw"}, + Extra: map[string]any{"add-extra": "ec83da", "conf-extra": "38srnw"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -58,7 +58,7 @@ func TestNotebookNewNote(t *testing.T) { Dir: "", Filename: "filename.ext", FilenameStem: "filename", - Extra: map[string]string{"add-extra": "ec83da", "conf-extra": "38srnw"}, + Extra: map[string]any{"add-extra": "ec83da", "conf-extra": "38srnw"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -80,7 +80,7 @@ func TestNotebookNewNoteWithDefaultTitle(t *testing.T) { newNoteTemplateContext{ ID: "id", Title: "Titre par défaut", - Extra: map[string]string{"conf-extra": "38srnw"}, + Extra: map[string]any{"conf-extra": "38srnw"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -128,7 +128,7 @@ func TestNotebookNewNoteInDir(t *testing.T) { Dir: "a-dir", Filename: "", FilenameStem: "", - Extra: map[string]string{"conf-extra": "38srnw"}, + Extra: map[string]any{"conf-extra": "38srnw"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -141,7 +141,7 @@ func TestNotebookNewNoteInDir(t *testing.T) { Dir: "a-dir", Filename: "filename.ext", FilenameStem: "filename", - Extra: map[string]string{"conf-extra": "38srnw"}, + Extra: map[string]any{"conf-extra": "38srnw"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -165,7 +165,7 @@ func TestNotebookNewNoteInDirWithGroup(t *testing.T) { Case: CaseMixed, }, }, - Extra: map[string]string{ + Extra: map[string]any{ "group-extra": "e48rs", }, } @@ -204,7 +204,7 @@ func TestNotebookNewNoteInDirWithGroup(t *testing.T) { Dir: "a-dir", Filename: "", FilenameStem: "", - Extra: map[string]string{"group-extra": "e48rs"}, + Extra: map[string]any{"group-extra": "e48rs"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -217,7 +217,7 @@ func TestNotebookNewNoteInDirWithGroup(t *testing.T) { Dir: "a-dir", Filename: "group-filename.group-ext", FilenameStem: "group-filename", - Extra: map[string]string{"group-extra": "e48rs"}, + Extra: map[string]any{"group-extra": "e48rs"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -240,7 +240,7 @@ func TestNotebookNewNoteWithGroup(t *testing.T) { Case: CaseMixed, }, }, - Extra: map[string]string{ + Extra: map[string]any{ "group-extra": "e48rs", }, } @@ -279,7 +279,7 @@ func TestNotebookNewNoteWithGroup(t *testing.T) { Dir: "", Filename: "", FilenameStem: "", - Extra: map[string]string{"group-extra": "e48rs"}, + Extra: map[string]any{"group-extra": "e48rs"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -292,7 +292,7 @@ func TestNotebookNewNoteWithGroup(t *testing.T) { Dir: "", Filename: "group-filename.group-ext", FilenameStem: "group-filename", - Extra: map[string]string{"group-extra": "e48rs"}, + Extra: map[string]any{"group-extra": "e48rs"}, Now: now, Env: map[string]string{"KEY1": "foo", "KEY2": "bar"}, }, @@ -459,7 +459,7 @@ func (t *newNoteTest) setup() { }, }, Groups: t.groups, - Extra: map[string]string{ + Extra: map[string]any{ "conf-extra": "38srnw", }, } diff --git a/internal/core/notebook.go b/internal/core/notebook.go index 1c6f820a..1ee4b187 100644 --- a/internal/core/notebook.go +++ b/internal/core/notebook.go @@ -96,7 +96,7 @@ type NewNoteOpts struct { // Path to a custom template used to render the note. Template opt.String // Extra variables passed to the templates. - Extra map[string]string + Extra map[string]any // Creation date provided to the templates. Date time.Time // Don't save the generated note on the file system. From 1fc45f981a593e6bc19ef90f960093bbc0bf563d Mon Sep 17 00:00:00 2001 From: Michael McDonagh Date: Tue, 16 Dec 2025 20:28:01 -0500 Subject: [PATCH 2/5] Add Extra JSON Parameter --- internal/cli/cmd/new.go | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/internal/cli/cmd/new.go b/internal/cli/cmd/new.go index afffedea..253e4151 100644 --- a/internal/cli/cmd/new.go +++ b/internal/cli/cmd/new.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "errors" "fmt" "io" @@ -16,16 +17,17 @@ import ( // New adds a new note to the notebook. type New struct { - Directory string `arg optional default:"." help:"Directory in which to create the note."` - Interactive bool `short:i help:"Read contents from standard input."` - 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]any ` help:"Extra variables passed to the templates." mapsep:","` - Template string ` placeholder:PATH help:"Custom template used to render the note."` - PrintPath bool `short:p help:"Print the path of the created note instead of editing it."` - DryRun bool `short:n help:"Don't actually create the note. Instead, prints its content on stdout and the generated path on stderr."` - ID string ` placeholder:ID help:"Skip id generation and use provided value."` + Directory string `arg optional default:"." help:"Directory in which to create the note."` + Interactive bool `short:i help:"Read contents from standard input."` + 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:","` + ExtraJSON string `name:"extra-json" help:"Extra variables passed to the templates, as a JSON object"` + Template string ` placeholder:PATH help:"Custom template used to render the note."` + PrintPath bool `short:p help:"Print the path of the created note instead of editing it."` + DryRun bool `short:n help:"Don't actually create the note. Instead, prints its content on stdout and the generated path on stderr."` + ID string ` placeholder:ID help:"Skip id generation and use provided value."` } func (cmd *New) Run(container *cli.Container) error { @@ -42,6 +44,20 @@ func (cmd *New) Run(container *cli.Container) error { } } + var extra map[string]any + if len(cmd.ExtraJSON) > 0 { + err = json.Unmarshal([]byte(cmd.ExtraJSON), &extra) + if err != nil { + return err + } + } + if extra == nil { + extra = make(map[string]any) + } + for k, v := range cmd.Extra { + extra[k] = v + } + date := time.Now() if cmd.Date != "" { date, err = dateutil.TimeFromNatural(cmd.Date) @@ -56,7 +72,7 @@ func (cmd *New) Run(container *cli.Container) error { Directory: opt.NewNotEmptyString(cmd.Directory), Group: opt.NewNotEmptyString(cmd.Group), Template: opt.NewNotEmptyString(cmd.Template), - Extra: cmd.Extra, + Extra: extra, Date: date, DryRun: cmd.DryRun, ID: cmd.ID, From 284889c6a40b43a105b232e2c6dba34fd5d3029a Mon Sep 17 00:00:00 2001 From: Michael McDonagh Date: Tue, 16 Dec 2025 20:47:22 -0500 Subject: [PATCH 3/5] Update documentation --- docs/config/config-extra.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/config/config-extra.md b/docs/config/config-extra.md index fe95df9f..1be0b772 100644 --- a/docs/config/config-extra.md +++ b/docs/config/config-extra.md @@ -17,6 +17,7 @@ section, which may override values from the root section. [extra] visibility = "public" author = "Mickaël" +authors = ["Thomas", "Aristotle"] [group.journal.extra] visibility = "private" # overrides @@ -33,6 +34,21 @@ $ zk new --extra author=Thomas $ zk new --extra show-header=1,author=Thomas ``` +`--extra-json` can also be used to pass a JSON object instead of command-line +arguments. The following commands are the JSON equivalent to the above ones: + +```sh +$ zk new --extra-json '{"author": "Thomas"}' +$ zk new --extra-json '{"show-header": "1", "author": "Thomas"}' +``` + +While the value passed to `--extra-json` must be a JSON object, its values need +not be: + +```sh +$ zk new --extra-json '{"show-header": 1, "authors": ["Thomas", "Aristotle"]}' +``` + ## Using extra variables in templates After declaring extra variables, you can expand them inside the @@ -45,4 +61,9 @@ After declaring extra variables, you can expand them inside the Written by {{extra.author}}. {{#if extra.show-header}} Behold, the mighty dynamic header! {{/if}} +{{#if extra.authors}} + {{#each}} + {{.}} + {{/each}} +{{/if}} ``` From 10f28beb94119da4de2b3bfabb7a5ac3e2bc029f Mon Sep 17 00:00:00 2001 From: Michael McDonagh Date: Tue, 16 Dec 2025 20:52:33 -0500 Subject: [PATCH 4/5] Update tesh --- tests/cmd-new.tesh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/cmd-new.tesh b/tests/cmd-new.tesh index ba6a154e..ba66c2bf 100644 --- a/tests/cmd-new.tesh +++ b/tests/cmd-new.tesh @@ -24,6 +24,8 @@ $ zk new --help > Takes precedence over the config of the > directory. > --extra=KEY=VALUE,... Extra variables passed to the templates. +> --extra-json=STRING Extra variables passed to the templates, as a +> JSON object > --template=PATH Custom template used to render the note. > -p, --print-path Print the path of the created note instead of > editing it. From 326e1f229171d9c3f0a42e854eb4e326f9bc1e86 Mon Sep 17 00:00:00 2001 From: Michael McDonagh Date: Tue, 16 Dec 2025 20:54:17 -0500 Subject: [PATCH 5/5] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 843b812e..e3e17ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Format: ` (, )` ## Unreleased +- Add --extra-json flag (by @mcDevnagh, #633) + ## Fixed - Release tarballs now output the program version (by @WhyNotHugo, 344b99f)