Index: LICENSE.txt ================================================================== --- LICENSE.txt +++ LICENSE.txt @@ -1,6 +1,6 @@ -Copyright (c) 2020-present Detlef Stern +Copyright (c) 2020-2023 Detlef Stern Licensed under the EUPL Zettelstore is licensed under the European Union Public License, version 1.2 or later (EUPL v. 1.2). The license is available in the official languages of the Index: Makefile ================================================================== --- Makefile +++ Makefile @@ -1,7 +1,7 @@ -## Copyright (c) 2020-present Detlef Stern +## Copyright (c) 2020-2022 Detlef Stern ## ## This file is part of Zettelstore. ## ## Zettelstore is licensed under the latest version of the EUPL (European Union ## Public License). Please see file LICENSE.txt for your rights and obligations Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -0.11.2 +0.10.0 Index: ast/ast.go ================================================================== --- ast/ast.go +++ ast/ast.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/block.go ================================================================== --- ast/block.go +++ ast/block.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/inline.go ================================================================== --- ast/inline.go +++ ast/inline.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/ref.go ================================================================== --- ast/ref.go +++ ast/ref.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/ref_test.go ================================================================== --- ast/ref_test.go +++ ast/ref_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/walk.go ================================================================== --- ast/walk.go +++ ast/walk.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: ast/walk_test.go ================================================================== --- ast/walk_test.go +++ ast/walk_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/auth.go ================================================================== --- auth/auth.go +++ auth/auth.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/cred/cred.go ================================================================== --- auth/cred/cred.go +++ auth/cred/cred.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/impl/impl.go ================================================================== --- auth/impl/impl.go +++ auth/impl/impl.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/anon.go ================================================================== --- auth/policy/anon.go +++ auth/policy/anon.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/box.go ================================================================== --- auth/policy/box.go +++ auth/policy/box.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/default.go ================================================================== --- auth/policy/default.go +++ auth/policy/default.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/owner.go ================================================================== --- auth/policy/owner.go +++ auth/policy/owner.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/policy.go ================================================================== --- auth/policy/policy.go +++ auth/policy/policy.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/policy_test.go ================================================================== --- auth/policy/policy_test.go +++ auth/policy/policy_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: auth/policy/readonly.go ================================================================== --- auth/policy/readonly.go +++ auth/policy/readonly.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/box.go ================================================================== --- box/box.go +++ box/box.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -14,10 +14,12 @@ import ( "context" "errors" "fmt" "io" + "net/url" + "strconv" "time" "zettelstore.de/c/api" "zettelstore.de/z/domain" "zettelstore.de/z/domain/id" @@ -90,32 +92,14 @@ // Zettel is the number of zettel managed by the box. Zettel int } -// StartState enumerates the possible states of starting and stopping a box. -// -// StartStateStopped -> StartStateStarting -> StartStateStarted -> StateStateStopping -> StartStateStopped. -// -// Other transitions are also possible. -type StartState uint8 - -// Constant values of StartState -const ( - StartStateStopped StartState = iota - StartStateStarting - StartStateStarted - StartStateStopping -) - // StartStopper performs simple lifecycle management. type StartStopper interface { - // State the current status of the box. - State() StartState - // Start the box. Now all other functions of the box are allowed. - // Starting a box, which is not in state StartStateStopped is not allowed. + // Starting an already started box is not allowed. Start(ctx context.Context) error // Stop the started box. Now only the Start() function is allowed. Stop(ctx context.Context) } @@ -197,18 +181,17 @@ type UpdateReason uint8 // Values for Reason const ( _ UpdateReason = iota - OnReady // Box is started and fully operational OnReload // Box was reloaded OnZettel // Something with a zettel happened ) // UpdateInfo contains all the data about a changed zettel. type UpdateInfo struct { - Box BaseBox + Box Box Reason UpdateReason Zid id.Zid } // UpdateFunc is a function to be called when a change is detected. @@ -312,5 +295,30 @@ // ErrInvalidID is returned if the zettel id is not appropriate for the box operation. type ErrInvalidID struct{ Zid id.Zid } func (err *ErrInvalidID) Error() string { return "invalid Zettel id: " + err.Zid.String() } + +// GetQueryBool is a helper function to extract bool values from a box URI. +func GetQueryBool(u *url.URL, key string) bool { + _, ok := u.Query()[key] + return ok +} + +// GetQueryInt is a helper function to extract int values of a specified range from a box URI. +func GetQueryInt(u *url.URL, key string, min, def, max int) int { + sVal := u.Query().Get(key) + if sVal == "" { + return def + } + iVal, err := strconv.Atoi(sVal) + if err != nil { + return def + } + if iVal < min { + return min + } + if iVal > max { + return max + } + return iVal +} Index: box/compbox/compbox.go ================================================================== --- box/compbox/compbox.go +++ box/compbox/compbox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/config.go ================================================================== --- box/compbox/config.go +++ box/compbox/config.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/keys.go ================================================================== --- box/compbox/keys.go +++ box/compbox/keys.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/log.go ================================================================== --- box/compbox/log.go +++ box/compbox/log.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/manager.go ================================================================== --- box/compbox/manager.go +++ box/compbox/manager.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/parser.go ================================================================== --- box/compbox/parser.go +++ box/compbox/parser.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/compbox/version.go ================================================================== --- box/compbox/version.go +++ box/compbox/version.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/constbox/base.css ================================================================== --- box/constbox/base.css +++ box/constbox/base.css @@ -142,11 +142,10 @@ padding-left: 1em; padding-right: 1em; } a:not([class]) { text-decoration-skip-ink: auto } a.broken { text-decoration: line-through } - a.external::after { content: "➚"; display: inline-block } img { max-width: 100% } img.right { float: right } ol.zs-endnotes { padding-top: .5rem; border-top: 1px solid; Index: box/constbox/constbox.go ================================================================== --- box/constbox/constbox.go +++ box/constbox/constbox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/constbox/context.mustache ================================================================== --- box/constbox/context.mustache +++ box/constbox/context.mustache @@ -1,6 +1,5 @@ -

{{Title}}

Info · Backward @@ -8,6 +7,5 @@ · Forward · Cost:{{#Costs}} {{{Text}}}{{/Costs}}
{{{Content}}} -
Index: box/constbox/delete.mustache ================================================================== --- box/constbox/delete.mustache +++ box/constbox/delete.mustache @@ -38,5 +38,6 @@
+{{end}} Index: box/constbox/license.txt ================================================================== --- box/constbox/license.txt +++ box/constbox/license.txt @@ -1,6 +1,6 @@ -Copyright (c) 2020-present Detlef Stern +Copyright (c) 2020-2023 Detlef Stern Licensed under the EUPL Zettelstore is licensed under the European Union Public License, version 1.2 or later (EUPL v. 1.2). The license is available in the official languages of the Index: box/constbox/listzettel.mustache ================================================================== --- box/constbox/listzettel.mustache +++ box/constbox/listzettel.mustache @@ -1,14 +1,12 @@ -

{{Title}}

{{{Content}}} -{{#CanCreate}}
+ -
{{/CanCreate}} -
+ Index: box/constbox/zettel.mustache ================================================================== --- box/constbox/zettel.mustache +++ box/constbox/zettel.mustache @@ -1,8 +1,8 @@
-

{{Heading}}

+

{{{HTMLTitle}}}

{{#CanWrite}}Edit ·{{/CanWrite}} {{Zid}} · Info · ({{RoleText}}) @@ -10,11 +10,11 @@ {{#CanCopy}}· Copy{{/CanCopy}} {{#CanVersion}}· Version{{/CanVersion}} {{#CanFolge}}· Folge{{/CanFolge}} {{#PredecessorRefs}}
Predecessor: {{{PredecessorRefs}}}{{/PredecessorRefs}} {{#PrecursorRefs}}
Precursor: {{{PrecursorRefs}}}{{/PrecursorRefs}} -{{#HasExtURL}}
URL: {{{ExtURL}}}{{/HasExtURL}} +{{#HasExtURL}}
URL: {{ExtURL}}{{/HasExtURL}} {{#Author}}
By {{Author}}{{/Author}}
{{{Content}}}
Index: box/dirbox/dirbox.go ================================================================== --- box/dirbox/dirbox.go +++ box/dirbox/dirbox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -127,28 +127,10 @@ func (dp *dirBox) Location() string { return dp.location } -func (dp *dirBox) State() box.StartState { - if ds := dp.dirSrv; ds != nil { - switch ds.State() { - case notify.DsCreated: - return box.StartStateStopped - case notify.DsStarting: - return box.StartStateStarting - case notify.DsWorking: - return box.StartStateStarted - case notify.DsMissing: - return box.StartStateStarted - case notify.DsStopping: - return box.StartStateStopping - } - } - return box.StartStateStopped -} - func (dp *dirBox) Start(context.Context) error { dp.mxCmds.Lock() defer dp.mxCmds.Unlock() dp.fCmds = make([]chan fileCmd, 0, dp.fSrvs) for i := uint32(0); i < dp.fSrvs; i++ { @@ -169,11 +151,10 @@ dp.log.Fatal().Err(err).Msg("Unable to create directory supervisor") dp.stopFileServices() return err } dp.dirSrv = notify.NewDirService( - dp, dp.log.Clone().Str("sub", "dirsrv").Child(), notifier, dp.cdata.Notify, ) dp.dirSrv.Start() Index: box/dirbox/dirbox_test.go ================================================================== --- box/dirbox/dirbox_test.go +++ box/dirbox/dirbox_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/dirbox/service.go ================================================================== --- box/dirbox/service.go +++ box/dirbox/service.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/filebox/filebox.go ================================================================== --- box/filebox/filebox.go +++ box/filebox/filebox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/filebox/zipbox.go ================================================================== --- box/filebox/zipbox.go +++ box/filebox/zipbox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -40,36 +40,18 @@ return "file://" + zb.name } return "file:" + zb.name } -func (zb *zipBox) State() box.StartState { - if ds := zb.dirSrv; ds != nil { - switch ds.State() { - case notify.DsCreated: - return box.StartStateStopped - case notify.DsStarting: - return box.StartStateStarting - case notify.DsWorking: - return box.StartStateStarted - case notify.DsMissing: - return box.StartStateStarted - case notify.DsStopping: - return box.StartStateStopping - } - } - return box.StartStateStopped -} - func (zb *zipBox) Start(context.Context) error { reader, err := zip.OpenReader(zb.name) if err != nil { return err } reader.Close() zipNotifier := notify.NewSimpleZipNotifier(zb.log, zb.name) - zb.dirSrv = notify.NewDirService(zb, zb.log, zipNotifier, zb.notify) + zb.dirSrv = notify.NewDirService(zb.log, zipNotifier, zb.notify) zb.dirSrv.Start() return nil } func (zb *zipBox) Refresh(_ context.Context) { @@ -77,11 +59,10 @@ zb.log.Trace().Msg("Refresh") } func (zb *zipBox) Stop(context.Context) { zb.dirSrv.Stop() - zb.dirSrv = nil } func (*zipBox) CanCreateZettel(context.Context) bool { return false } func (zb *zipBox) CreateZettel(context.Context, domain.Zettel) (id.Zid, error) { Index: box/helper.go ================================================================== --- box/helper.go +++ box/helper.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -9,12 +9,10 @@ //----------------------------------------------------------------------------- package box import ( - "net/url" - "strconv" "time" "zettelstore.de/z/domain/id" ) @@ -34,30 +32,5 @@ time.Sleep(100 * time.Millisecond) withSeconds = true } return id.Invalid, ErrConflict } - -// GetQueryBool is a helper function to extract bool values from a box URI. -func GetQueryBool(u *url.URL, key string) bool { - _, ok := u.Query()[key] - return ok -} - -// GetQueryInt is a helper function to extract int values of a specified range from a box URI. -func GetQueryInt(u *url.URL, key string, min, def, max int) int { - sVal := u.Query().Get(key) - if sVal == "" { - return def - } - iVal, err := strconv.Atoi(sVal) - if err != nil { - return def - } - if iVal < min { - return min - } - if iVal > max { - return max - } - return iVal -} Index: box/manager/anteroom.go ================================================================== --- box/manager/anteroom.go +++ box/manager/anteroom.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/anteroom_test.go ================================================================== --- box/manager/anteroom_test.go +++ box/manager/anteroom_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/box.go ================================================================== --- box/manager/box.go +++ box/manager/box.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -9,13 +9,13 @@ //----------------------------------------------------------------------------- package manager import ( + "bytes" "context" "errors" - "strings" "zettelstore.de/z/box" "zettelstore.de/z/domain" "zettelstore.de/z/domain/id" "zettelstore.de/z/domain/meta" @@ -27,46 +27,46 @@ // Location returns some information where the box is located. func (mgr *Manager) Location() string { if len(mgr.boxes) <= 2 { return "NONE" } - var sb strings.Builder + var buf bytes.Buffer for i := 0; i < len(mgr.boxes)-2; i++ { if i > 0 { - sb.WriteString(", ") + buf.WriteString(", ") } - sb.WriteString(mgr.boxes[i].Location()) + buf.WriteString(mgr.boxes[i].Location()) } - return sb.String() + return buf.String() } // CanCreateZettel returns true, if box could possibly create a new zettel. func (mgr *Manager) CanCreateZettel(ctx context.Context) bool { mgr.mgrMx.RLock() defer mgr.mgrMx.RUnlock() - return mgr.State() == box.StartStateStarted && mgr.boxes[0].CanCreateZettel(ctx) + return mgr.started && mgr.boxes[0].CanCreateZettel(ctx) } // CreateZettel creates a new zettel. func (mgr *Manager) CreateZettel(ctx context.Context, zettel domain.Zettel) (id.Zid, error) { mgr.mgrLog.Debug().Msg("CreateZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return id.Invalid, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() return mgr.boxes[0].CreateZettel(ctx, zettel) } // GetZettel retrieves a specific zettel. func (mgr *Manager) GetZettel(ctx context.Context, zid id.Zid) (domain.Zettel, error) { mgr.mgrLog.Debug().Zid(zid).Msg("GetZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return domain.Zettel{}, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for i, p := range mgr.boxes { if z, err := p.GetZettel(ctx, zid); err != box.ErrNotFound { if err == nil { mgr.Enrich(ctx, z.Meta, i+1) } @@ -77,15 +77,15 @@ } // GetAllZettel retrieves a specific zettel from all managed boxes. func (mgr *Manager) GetAllZettel(ctx context.Context, zid id.Zid) ([]domain.Zettel, error) { mgr.mgrLog.Debug().Zid(zid).Msg("GetAllZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return nil, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() var result []domain.Zettel for i, p := range mgr.boxes { if z, err := p.GetZettel(ctx, zid); err == nil { mgr.Enrich(ctx, z.Meta, i+1) result = append(result, z) @@ -95,15 +95,15 @@ } // GetMeta retrieves just the meta data of a specific zettel. func (mgr *Manager) GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error) { mgr.mgrLog.Debug().Zid(zid).Msg("GetMeta") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return nil, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() return mgr.doGetMeta(ctx, zid) } func (mgr *Manager) doGetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error) { for i, p := range mgr.boxes { @@ -118,15 +118,15 @@ } // GetAllMeta retrieves the meta data of a specific zettel from all managed boxes. func (mgr *Manager) GetAllMeta(ctx context.Context, zid id.Zid) ([]*meta.Meta, error) { mgr.mgrLog.Debug().Zid(zid).Msg("GetAllMeta") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return nil, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() var result []*meta.Meta for i, p := range mgr.boxes { if m, err := p.GetMeta(ctx, zid); err == nil { mgr.Enrich(ctx, m, i+1) result = append(result, m) @@ -136,16 +136,16 @@ } // FetchZids returns the set of all zettel identifer managed by the box. func (mgr *Manager) FetchZids(ctx context.Context) (id.Set, error) { mgr.mgrLog.Debug().Msg("FetchZids") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return nil, box.ErrStopped } result := id.Set{} - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for _, p := range mgr.boxes { err := p.ApplyZid(ctx, func(zid id.Zid) { result.Zid(zid) }, func(id.Zid) bool { return true }) if err != nil { return nil, err } @@ -159,15 +159,15 @@ // criteria. The result is ordered by descending zettel id. func (mgr *Manager) SelectMeta(ctx context.Context, q *query.Query) ([]*meta.Meta, error) { if msg := mgr.mgrLog.Debug(); msg.Enabled() { msg.Str("query", q.String()).Msg("SelectMeta") } - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return nil, box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() compSearch := q.RetrieveAndCompile(mgr) selected := metaMap{} for _, term := range compSearch.Terms { rejected := id.Set{} @@ -197,24 +197,26 @@ } result := make([]*meta.Meta, 0, len(selected)) for _, m := range selected { result = append(result, m) } - return q.AfterSearch(result), nil + return q.Sort(result), nil } // CanUpdateZettel returns true, if box could possibly update the given zettel. func (mgr *Manager) CanUpdateZettel(ctx context.Context, zettel domain.Zettel) bool { mgr.mgrMx.RLock() defer mgr.mgrMx.RUnlock() - return mgr.State() == box.StartStateStarted && mgr.boxes[0].CanUpdateZettel(ctx, zettel) + return mgr.started && mgr.boxes[0].CanUpdateZettel(ctx, zettel) } // UpdateZettel updates an existing zettel. func (mgr *Manager) UpdateZettel(ctx context.Context, zettel domain.Zettel) error { mgr.mgrLog.Debug().Zid(zettel.Meta.Zid).Msg("UpdateZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return box.ErrStopped } // Remove all (computed) properties from metadata before storing the zettel. zettel.Meta = zettel.Meta.Clone() for _, p := range zettel.Meta.ComputedPairsRest() { @@ -225,15 +227,15 @@ return mgr.boxes[0].UpdateZettel(ctx, zettel) } // AllowRenameZettel returns true, if box will not disallow renaming the zettel. func (mgr *Manager) AllowRenameZettel(ctx context.Context, zid id.Zid) bool { - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return false } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for _, p := range mgr.boxes { if !p.AllowRenameZettel(ctx, zid) { return false } } @@ -241,15 +243,15 @@ } // RenameZettel changes the current zid to a new zid. func (mgr *Manager) RenameZettel(ctx context.Context, curZid, newZid id.Zid) error { mgr.mgrLog.Debug().Zid(curZid).Zid(newZid).Msg("RenameZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for i, p := range mgr.boxes { err := p.RenameZettel(ctx, curZid, newZid) if err != nil && !errors.Is(err, box.ErrNotFound) { for j := 0; j < i; j++ { mgr.boxes[j].RenameZettel(ctx, newZid, curZid) @@ -260,15 +262,15 @@ return nil } // CanDeleteZettel returns true, if box could possibly delete the given zettel. func (mgr *Manager) CanDeleteZettel(ctx context.Context, zid id.Zid) bool { - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return false } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for _, p := range mgr.boxes { if p.CanDeleteZettel(ctx, zid) { return true } } @@ -276,15 +278,15 @@ } // DeleteZettel removes the zettel from the box. func (mgr *Manager) DeleteZettel(ctx context.Context, zid id.Zid) error { mgr.mgrLog.Debug().Zid(zid).Msg("DeleteZettel") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.RLock() + defer mgr.mgrMx.RUnlock() + if !mgr.started { return box.ErrStopped } - mgr.mgrMx.RLock() - defer mgr.mgrMx.RUnlock() for _, p := range mgr.boxes { err := p.DeleteZettel(ctx, zid) if err == nil { return nil } Index: box/manager/collect.go ================================================================== --- box/manager/collect.go +++ box/manager/collect.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/enrich.go ================================================================== --- box/manager/enrich.go +++ box/manager/enrich.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/indexer.go ================================================================== --- box/manager/indexer.go +++ box/manager/indexer.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -83,14 +83,15 @@ timerDuration := 15 * time.Second timer := time.NewTimer(timerDuration) ctx := box.NoEnrichContext(context.Background()) for { - mgr.idxWorkService(ctx) + // Sleep first, so the indexer will wait for boxes to initialize. if !mgr.idxSleepService(timer, timerDuration) { return } + mgr.idxWorkService(ctx) } } func (mgr *Manager) idxWorkService(ctx context.Context) { var roomNum uint64 Index: box/manager/manager.go ================================================================== --- box/manager/manager.go +++ box/manager/manager.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -83,13 +83,12 @@ func GetSchemes() []string { return maps.Keys(registry) } // Manager is a coordinating box. type Manager struct { mgrLog *logger.Logger - stateMx sync.RWMutex - state box.StartState mgrMx sync.RWMutex + started bool rtConfig config.Config boxes []box.ManagedBox observers []box.UpdateFunc mxObserver sync.RWMutex done chan struct{} @@ -107,23 +106,10 @@ idxLastReload time.Time idxDurReload time.Duration idxSinceReload uint64 } -func (mgr *Manager) setState(newState box.StartState) { - mgr.stateMx.Lock() - mgr.state = newState - mgr.stateMx.Unlock() -} - -func (mgr *Manager) State() box.StartState { - mgr.stateMx.RLock() - state := mgr.state - mgr.stateMx.RUnlock() - return state -} - // New creates a new managing box. func New(boxURIs []*url.URL, authManager auth.BaseManager, rtConfig config.Config) (*Manager, error) { descrs := meta.GetSortedKeyDescriptions() propertyKeys := make(strfun.Set, len(descrs)) for _, kd := range descrs { @@ -207,18 +193,15 @@ mgr.mgrLog.Debug().Uint("reason", uint64(reason)).Zid(zid).Msg("notifier") if ignoreUpdate(cache, now, reason, zid) { mgr.mgrLog.Trace().Uint("reason", uint64(reason)).Zid(zid).Msg("notifier ignored") continue } - mgr.idxEnqueue(reason, zid) if ci.Box == nil { ci.Box = mgr } - if mgr.State() == box.StartStateStarted { - mgr.notifyObserver(&ci) - } + mgr.notifyObserver(&ci) } case <-mgr.done: return } } @@ -243,18 +226,15 @@ return false } func (mgr *Manager) idxEnqueue(reason box.UpdateReason, zid id.Zid) { switch reason { - case box.OnReady: - return case box.OnReload: mgr.idxAr.Reset() case box.OnZettel: mgr.idxAr.EnqueueZettel(zid) default: - mgr.mgrLog.Warn().Uint("reason", uint64(reason)).Zid(zid).Msg("Unknown notification reason") return } select { case mgr.idxReady <- struct{}{}: default: @@ -272,82 +252,65 @@ // Start the box. Now all other functions of the box are allowed. // Starting an already started box is not allowed. func (mgr *Manager) Start(ctx context.Context) error { mgr.mgrMx.Lock() - defer mgr.mgrMx.Unlock() - if mgr.State() != box.StartStateStopped { + if mgr.started { + mgr.mgrMx.Unlock() return box.ErrStarted } - mgr.setState(box.StartStateStarting) for i := len(mgr.boxes) - 1; i >= 0; i-- { ssi, ok := mgr.boxes[i].(box.StartStopper) if !ok { continue } err := ssi.Start(ctx) if err == nil { continue } - mgr.setState(box.StartStateStopping) for j := i + 1; j < len(mgr.boxes); j++ { if ssj, ok2 := mgr.boxes[j].(box.StartStopper); ok2 { ssj.Stop(ctx) } } - mgr.setState(box.StartStateStopped) + mgr.mgrMx.Unlock() return err } mgr.idxAr.Reset() // Ensure an initial index run mgr.done = make(chan struct{}) go mgr.notifier() - - for !mgr.allBoxesStarted() { - mgr.mgrLog.Trace().Msg("Wait for boxes to start") - time.Sleep(time.Second) - } - mgr.setState(box.StartStateStarted) - mgr.notifyObserver(&box.UpdateInfo{Box: mgr, Reason: box.OnReady}) - go mgr.idxIndexer() - return nil -} - -func (mgr *Manager) allBoxesStarted() bool { - for _, bx := range mgr.boxes { - if b, ok := bx.(box.StartStopper); ok && b.State() != box.StartStateStarted { - return false - } - } - return true + + mgr.started = true + mgr.mgrMx.Unlock() + return nil } // Stop the started box. Now only the Start() function is allowed. func (mgr *Manager) Stop(ctx context.Context) { mgr.mgrMx.Lock() defer mgr.mgrMx.Unlock() - if mgr.State() != box.StartStateStarted { + if !mgr.started { return } - mgr.setState(box.StartStateStopping) close(mgr.done) for _, p := range mgr.boxes { if ss, ok := p.(box.StartStopper); ok { ss.Stop(ctx) } } - mgr.setState(box.StartStateStopped) + mgr.started = false } // Refresh internal box data. func (mgr *Manager) Refresh(ctx context.Context) error { mgr.mgrLog.Debug().Msg("Refresh") - if mgr.State() != box.StartStateStarted { + mgr.mgrMx.Lock() + defer mgr.mgrMx.Unlock() + if !mgr.started { return box.ErrStopped } - mgr.mgrMx.Lock() - defer mgr.mgrMx.Unlock() mgr.infos <- box.UpdateInfo{Reason: box.OnReload, Zid: id.Invalid} for _, bx := range mgr.boxes { if rb, ok := bx.(box.Refresher); ok { rb.Refresh(ctx) } Index: box/manager/memstore/memstore.go ================================================================== --- box/manager/memstore/memstore.go +++ box/manager/memstore/memstore.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/memstore/refs.go ================================================================== --- box/manager/memstore/refs.go +++ box/manager/memstore/refs.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/memstore/refs_test.go ================================================================== --- box/manager/memstore/refs_test.go +++ box/manager/memstore/refs_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/store/store.go ================================================================== --- box/manager/store/store.go +++ box/manager/store/store.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/store/wordset.go ================================================================== --- box/manager/store/wordset.go +++ box/manager/store/wordset.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/store/wordset_test.go ================================================================== --- box/manager/store/wordset_test.go +++ box/manager/store/wordset_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/manager/store/zettel.go ================================================================== --- box/manager/store/zettel.go +++ box/manager/store/zettel.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/membox/membox.go ================================================================== --- box/membox/membox.go +++ box/membox/membox.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -52,27 +52,18 @@ curBytes int } func (mb *memBox) notifyChanged(zid id.Zid) { if chci := mb.cdata.Notify; chci != nil { - chci <- box.UpdateInfo{Box: mb, Reason: box.OnZettel, Zid: zid} + chci <- box.UpdateInfo{Reason: box.OnZettel, Zid: zid} } } func (mb *memBox) Location() string { return mb.u.String() } -func (mb *memBox) State() box.StartState { - mb.mx.RLock() - defer mb.mx.RUnlock() - if mb.zettel == nil { - return box.StartStateStopped - } - return box.StartStateStarted -} - func (mb *memBox) Start(context.Context) error { mb.mx.Lock() mb.zettel = make(map[id.Zid]domain.Zettel) mb.curBytes = 0 mb.mx.Unlock() Index: box/notify/directory.go ================================================================== --- box/notify/directory.go +++ box/notify/directory.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -27,67 +27,57 @@ "zettelstore.de/z/strfun" ) type entrySet map[id.Zid]*DirEntry -// DirServiceState signal the internal state of the service. +// directoryState signal the internal state of the service. // // The following state transitions are possible: // --newDirService--> dsCreated // dsCreated --Start--> dsStarting // dsStarting --last list notification--> dsWorking // dsWorking --directory missing--> dsMissing // dsMissing --last list notification--> dsWorking // --Stop--> dsStopping -type DirServiceState uint8 +type directoryState uint8 const ( - DsCreated DirServiceState = iota - DsStarting // Reading inital scan - DsWorking // Initial scan complete, fully operational - DsMissing // Directory is missing - DsStopping // Service is shut down + dsCreated directoryState = iota + dsStarting // Reading inital scan + dsWorking // Initial scan complete, fully operational + dsMissing // Directory is missing + dsStopping // Service is shut down ) // DirService specifies a directory service for file based zettel. type DirService struct { - box box.ManagedBox log *logger.Logger dirPath string notifier Notifier infos chan<- box.UpdateInfo mx sync.RWMutex // protects status, entries - state DirServiceState + state directoryState entries entrySet } // ErrNoDirectory signals missing directory data. var ErrNoDirectory = errors.New("unable to retrieve zettel directory information") // NewDirService creates a new directory service. -func NewDirService(box box.ManagedBox, log *logger.Logger, notifier Notifier, chci chan<- box.UpdateInfo) *DirService { +func NewDirService(log *logger.Logger, notifier Notifier, chci chan<- box.UpdateInfo) *DirService { return &DirService{ - box: box, log: log, notifier: notifier, infos: chci, - state: DsCreated, - } -} - -// State the current service state. -func (ds *DirService) State() DirServiceState { - ds.mx.RLock() - state := ds.state - ds.mx.RUnlock() - return state + state: dsCreated, + } } // Start the directory service. func (ds *DirService) Start() { ds.mx.Lock() - ds.state = DsStarting + ds.state = dsStarting ds.mx.Unlock() var newEntries entrySet go ds.updateEvents(newEntries) } @@ -97,11 +87,11 @@ } // Stop the directory service. func (ds *DirService) Stop() { ds.mx.Lock() - ds.state = DsStopping + ds.state = dsStopping ds.mx.Unlock() ds.notifier.Close() } func (ds *DirService) logMissingEntry(action string) error { @@ -247,30 +237,30 @@ ds.mx.RUnlock() if msg := ds.log.Trace(); msg.Enabled() { msg.Uint("state", uint64(state)).Str("op", ev.Op.String()).Str("name", ev.Name).Msg("notifyEvent") } - if state == DsStopping { + if state == dsStopping { return nil, false } switch ev.Op { case Error: newEntries = nil - if state != DsMissing { + if state != dsMissing { ds.log.Warn().Err(ev.Err).Msg("Notifier confused") } case Make: newEntries = make(entrySet) case List: if ev.Name == "" { zids := getNewZids(newEntries) ds.mx.Lock() - fromMissing := ds.state == DsMissing + fromMissing := ds.state == dsMissing prevEntries := ds.entries ds.entries = newEntries - ds.state = DsWorking + ds.state = dsWorking ds.mx.Unlock() ds.onCreateDirectory(zids, prevEntries) if fromMissing { ds.log.Info().Str("path", ds.dirPath).Msg("Zettel directory found") } @@ -326,11 +316,11 @@ func (ds *DirService) onDestroyDirectory() { ds.mx.Lock() entries := ds.entries ds.entries = nil - ds.state = DsMissing + ds.state = dsMissing ds.mx.Unlock() for zid := range entries { ds.notifyChange(zid) } } @@ -603,8 +593,8 @@ } func (ds *DirService) notifyChange(zid id.Zid) { if chci := ds.infos; chci != nil { ds.log.Trace().Zid(zid).Msg("notifyChange") - chci <- box.UpdateInfo{Box: ds.box, Reason: box.OnZettel, Zid: zid} + chci <- box.UpdateInfo{Reason: box.OnZettel, Zid: zid} } } Index: box/notify/directory_test.go ================================================================== --- box/notify/directory_test.go +++ box/notify/directory_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2022-present Detlef Stern +// Copyright (c) 2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/notify/entry.go ================================================================== --- box/notify/entry.go +++ box/notify/entry.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/notify/fsdir.go ================================================================== --- box/notify/fsdir.go +++ box/notify/fsdir.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -92,11 +92,10 @@ defer close(fsdn.events) defer close(fsdn.refresh) if !listDirElements(fsdn.log, fsdn.fetcher, fsdn.events, fsdn.done) { return } - for fsdn.readAndProcessEvent() { } } func (fsdn *fsdirNotifier) readAndProcessEvent() bool { Index: box/notify/helper.go ================================================================== --- box/notify/helper.go +++ box/notify/helper.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/notify/notify.go ================================================================== --- box/notify/notify.go +++ box/notify/notify.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: box/notify/simpledir.go ================================================================== --- box/notify/simpledir.go +++ box/notify/simpledir.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: cmd/cmd_file.go ================================================================== --- cmd/cmd_file.go +++ cmd/cmd_file.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: cmd/cmd_password.go ================================================================== --- cmd/cmd_password.go +++ cmd/cmd_password.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: cmd/cmd_run.go ================================================================== --- cmd/cmd_run.go +++ cmd/cmd_run.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -89,18 +89,20 @@ webSrv.Handle("/favicon.ico", wui.MakeFaviconHandler(assetDir)) } // Web user interface if !authManager.IsReadonly() { - webSrv.AddZettelRoute('b', server.MethodGet, wui.MakeGetRenameZettelHandler(ucGetMeta)) + webSrv.AddZettelRoute('b', server.MethodGet, wui.MakeGetRenameZettelHandler( + ucGetMeta, &ucEvaluate)) webSrv.AddZettelRoute('b', server.MethodPost, wui.MakePostRenameZettelHandler(&ucRename)) webSrv.AddListRoute('c', server.MethodGet, wui.MakeGetZettelFromListHandler(ucListMeta, &ucEvaluate, ucListRoles, ucListSyntax)) webSrv.AddListRoute('c', server.MethodPost, wui.MakePostCreateZettelHandler(&ucCreateZettel)) webSrv.AddZettelRoute('c', server.MethodGet, wui.MakeGetCreateZettelHandler( ucGetZettel, &ucCreateZettel, ucListRoles, ucListSyntax)) webSrv.AddZettelRoute('c', server.MethodPost, wui.MakePostCreateZettelHandler(&ucCreateZettel)) - webSrv.AddZettelRoute('d', server.MethodGet, wui.MakeGetDeleteZettelHandler(ucGetMeta, ucGetAllMeta)) + webSrv.AddZettelRoute('d', server.MethodGet, wui.MakeGetDeleteZettelHandler( + ucGetMeta, ucGetAllMeta, &ucEvaluate)) webSrv.AddZettelRoute('d', server.MethodPost, wui.MakePostDeleteZettelHandler(&ucDelete)) webSrv.AddZettelRoute('e', server.MethodGet, wui.MakeEditGetZettelHandler(ucGetZettel, ucListRoles, ucListSyntax)) webSrv.AddZettelRoute('e', server.MethodPost, wui.MakeEditSetZettelHandler(&ucUpdate)) } webSrv.AddListRoute('g', server.MethodGet, wui.MakeGetGoActionHandler(&ucRefresh)) @@ -116,11 +118,12 @@ // API webSrv.AddListRoute('a', server.MethodPost, a.MakePostLoginHandler(&ucAuthenticate)) webSrv.AddListRoute('a', server.MethodPut, a.MakeRenewAuthHandler()) webSrv.AddZettelRoute('o', server.MethodGet, a.MakeGetOrderHandler( usecase.NewZettelOrder(protectedBoxManager, ucEvaluate))) - webSrv.AddZettelRoute('u', server.MethodGet, a.MakeListUnlinkedMetaHandler(ucGetMeta, ucUnlinkedRefs)) + webSrv.AddZettelRoute('u', server.MethodGet, a.MakeListUnlinkedMetaHandler( + ucGetMeta, ucUnlinkedRefs, &ucEvaluate)) webSrv.AddListRoute('x', server.MethodGet, a.MakeGetDataHandler(ucVersion)) webSrv.AddListRoute('x', server.MethodPost, a.MakePostCommandHandler(&ucIsAuth, &ucRefresh)) webSrv.AddZettelRoute('x', server.MethodGet, a.MakeZettelContextHandler(ucZettelContext)) webSrv.AddListRoute('z', server.MethodGet, a.MakeQueryHandler(ucListMeta)) webSrv.AddZettelRoute('z', server.MethodGet, a.MakeGetZettelHandler(ucGetMeta, ucGetZettel, ucParseZettel, ucEvaluate)) Index: cmd/command.go ================================================================== --- cmd/command.go +++ cmd/command.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: cmd/main.go ================================================================== --- cmd/main.go +++ cmd/main.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: cmd/register.go ================================================================== --- cmd/register.go +++ cmd/register.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -19,16 +19,16 @@ _ "zettelstore.de/z/box/filebox" // Allow to use file box. _ "zettelstore.de/z/box/membox" // Allow to use in-memory box. _ "zettelstore.de/z/encoder/htmlenc" // Allow to use HTML encoder. _ "zettelstore.de/z/encoder/mdenc" // Allow to use markdown encoder. _ "zettelstore.de/z/encoder/sexprenc" // Allow to use sexpr encoder. - _ "zettelstore.de/z/encoder/shtmlenc" // Allow to use SHTML encoder. _ "zettelstore.de/z/encoder/textenc" // Allow to use text encoder. + _ "zettelstore.de/z/encoder/zjsonenc" // Allow to use ZJSON encoder. _ "zettelstore.de/z/encoder/zmkenc" // Allow to use zmk encoder. _ "zettelstore.de/z/kernel/impl" // Allow kernel implementation to create itself _ "zettelstore.de/z/parser/blob" // Allow to use BLOB parser. _ "zettelstore.de/z/parser/draw" // Allow to use draw parser. _ "zettelstore.de/z/parser/markdown" // Allow to use markdown parser. _ "zettelstore.de/z/parser/none" // Allow to use none parser. _ "zettelstore.de/z/parser/plain" // Allow to use plain parser. _ "zettelstore.de/z/parser/zettelmark" // Allow to use zettelmark parser. ) Index: cmd/zettelstore/main.go ================================================================== --- cmd/zettelstore/main.go +++ cmd/zettelstore/main.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: collect/collect.go ================================================================== --- collect/collect.go +++ collect/collect.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: collect/collect_test.go ================================================================== --- collect/collect_test.go +++ collect/collect_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: collect/order.go ================================================================== --- collect/order.go +++ collect/order.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: collect/split.go ================================================================== --- collect/split.go +++ collect/split.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: config/config.go ================================================================== --- config/config.go +++ config/config.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -20,10 +20,11 @@ // Key values that are supported by Config.Get const ( KeyFooterZettel = "footer-zettel" KeyHomeZettel = "home-zettel" // api.KeyLang + KeyMarkerExternal = "marker-external" ) // Config allows to retrieve all defined configuration values that can be changed during runtime. type Config interface { AuthConfig Index: docs/development/20210916193200.zettel ================================================================== --- docs/development/20210916193200.zettel +++ docs/development/20210916193200.zettel @@ -1,23 +1,22 @@ id: 20210916193200 title: Required Software role: zettel syntax: zmk created: 20210916193200 -modified: 20230327165135 +modified: 20230109121417 The following software must be installed: * A current, supported [[release of Go|https://go.dev/doc/devel/release]], * [[shadow|https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shadow]] via ``go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest``, * [[staticcheck|https://staticcheck.io/]] via ``go install honnef.co/go/tools/cmd/staticcheck@latest``, * [[unparam|https://mvdan.cc/unparam]][^[[GitHub|https://github.com/mvdan/unparam]]] via ``go install mvdan.cc/unparam@latest``, -* [[govulncheck|https://golang.org/x/vuln/cmd/govulncheck]] via ``go install golang.org/x/vuln/cmd/govulncheck@latest``, * [[Fossil|https://fossil-scm.org/]], * [[Git|https://git-scm.org/]] (most dependencies are accessible via Git only). Make sure that the software is in your path, e.g. via: ```sh export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:$(go env GOPATH)/bin ``` Index: docs/manual/00000000000100.zettel ================================================================== --- docs/manual/00000000000100.zettel +++ docs/manual/00000000000100.zettel @@ -1,14 +1,14 @@ id: 00000000000100 title: Zettelstore Runtime Configuration role: configuration syntax: none created: 00010101000000 -default-copyright: (c) 2020-present by Detlef Stern +default-copyright: (c) 2020-2022 by Detlef Stern default-license: EUPL-1.2-or-later default-visibility: public footer-zettel: 00001000000100 home-zettel: 00001000000000 modified: 20221205173642 site-name: Zettelstore Manual visibility: owner Index: docs/manual/00001004020000.zettel ================================================================== --- docs/manual/00001004020000.zettel +++ docs/manual/00001004020000.zettel @@ -2,11 +2,11 @@ title: Configure the running Zettelstore role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 -modified: 20230317183435 +modified: 20221219175720 You can configure a running Zettelstore by modifying the special zettel with the ID [[00000000000100]]. This zettel is called __configuration zettel__. The following metadata keys change the appearance / behavior of Zettelstore. Some of them can be overwritten in an [[user zettel|00001010040200]], a subset of those may be overwritten in zettel that is currently used. @@ -49,10 +49,16 @@ This value is used as a default value, if it is not set in an user's zettel or in a zettel. It is also used to specify the language for all non-zettel content, e.g. lists or search results. Use values according to the language definition of [[RFC-5646|https://tools.ietf.org/html/rfc5646]]. +; [!marker-external|''marker-external''] +: Some HTML code that is displayed after a [[reference to external material|00001007040310]]. + + May be [[overwritten|00001004020200]] in a user zettel. + + Default: ""&\#10138;"", to display a ""➚"" sign. ; [!max-transclusions|''max-transclusions''] : Maximum number of indirect transclusion. This is used to avoid an exploding ""transclusion bomb"", a form of a [[billion laughs attack|https://en.wikipedia.org/wiki/Billion_laughs_attack]]. Default: ""1024"". ; [!site-name|''site-name''] Index: docs/manual/00001004020200.zettel ================================================================== --- docs/manual/00001004020200.zettel +++ docs/manual/00001004020200.zettel @@ -2,11 +2,11 @@ title: Runtime configuration data that may be user specific or zettel specific role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20221205155521 -modified: 20230317183403 +modified: 20221212195530 Some metadata of the [[runtime configuration|00001004020000]] may be overwritten in an [[user zettel|00001010040200]]. A subset of those may be overwritten in zettel that is currently used. This allows to specify user specific or zettel specific behavior. @@ -14,5 +14,6 @@ |=Key|User:|Zettel:|Remarks |[[''footer-zettel''|00001004020000#footer-zettel]]|Y|N| |[[''home-zettel''|00001004020000#home-zettel]]|Y|N| |[[''lang''|00001004020000#lang]]|Y|Y|Making it user-specific could make zettel for other user less useful +|[[''marker-external''|00001004020000#marker-external]]|Y|Y| Index: docs/manual/00001004051200.zettel ================================================================== --- docs/manual/00001004051200.zettel +++ docs/manual/00001004051200.zettel @@ -2,11 +2,11 @@ title: The ''file'' sub-command role: manual tags: #command #configuration #manual #zettelstore syntax: zmk created: 20210126175322 -modified: 20230316182711 +modified: 20230109105434 Reads zettel data from a file (or from standard input / stdin) and renders it to standard output / stdout. This allows Zettelstore to render files manually. ``` zettelstore file [-t FORMAT] [file-1 [file-2]] @@ -16,15 +16,15 @@ : Specifies the output format. Supported values are: [[''html''|00001012920510]] (default), [[''md''|00001012920513]], [[''sexpr''|00001012920516]], - [[''shtml''|00001012920525]], [[''text''|00001012920519]], + [[''zjson''|00001012920503]] (deprecated in v0.11), and [[''zmk''|00001012920522]]. ; ''file-1'' : Specifies the file name, where at least metadata is read. If ''file-2'' is not given, the zettel content is also read from here. ; ''file-2'' : File name where the zettel content is stored. If neither ''file-1'' nor ''file-2'' are given, metadata and zettel content are read from standard input / stdin. Index: docs/manual/00001006020000.zettel ================================================================== --- docs/manual/00001006020000.zettel +++ docs/manual/00001006020000.zettel @@ -2,11 +2,11 @@ title: Supported Metadata Keys role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210126175322 -modified: 20230320150950 +modified: 20221004134841 Although you are free to define your own metadata, by using any key (according to the [[syntax|00001006010000]]), some keys have a special meaning that is enforced by Zettelstore. See the [[computed list of supported metadata keys|00000000000090]] for details. Most keys conform to a [[type|00001006030000]]. @@ -83,13 +83,10 @@ It can be used for [[sorting|00001007700000]] zettel based on their publication date. It is a computed value. There is no need to set it via Zettelstore. -; [!query|''query''] -: Stores the [[query|00001007031140]] that was used to create the zettel. - This is for future reference. ; [!read-only|''read-only''] : Marks a zettel as read-only. The interpretation of [[supported values|00001006020400]] for this key depends, whether authentication is [[enabled|00001010040100]] or not. ; [!role|''role''] : Defines the role of the zettel. @@ -100,13 +97,10 @@ : Is a property that contains identifier of all zettel that reference this zettel through the [[''predecessor''|#predecessor]] value. Therefore, it references all zettel that contain a new version of the content and/or metadata. In contrast to [[''folge''|#folge]], these are references because of technical reasons, not because of content-related reasons. In most cases, zettel referencing the current zettel should be updated to reference a successor zettel. The [[query reference|00001007040310]] [[query:backward? successors?]] lists all such zettel. -; [!summary|''summary''] -: Summarizes the content of the zettel. - You may use all [[inline-structued elements|00001007040000]] of Zettelmarkup. ; [!syntax|''syntax''] : Specifies the syntax that should be used for interpreting the zettel. The zettel about [[other markup languages|00001008000000]] defines supported values. If it is not given, it defaults to ''plain''. ; [!tags|''tags''] @@ -113,10 +107,12 @@ : Contains a space separated list of tags to describe the zettel further. Each Tag must begin with the number sign character (""''#''"", U+0023). ; [!title|''title''] : Specifies the title of the zettel. If not given, the value of [[''id''|#id]] will be used. + + You can use all [[inline-structured elements|00001007040000]] of Zettelmarkup. ; [!url|''url''] : Defines an URL / URI for this zettel that possibly references external material. One use case is to specify the document that the current zettel comments on. The URL will be rendered special in the [[web user interface|00001014000000]] if you use the default template. ; [!useless-files|''useless-files''] Index: docs/manual/00001007702000.zettel ================================================================== --- docs/manual/00001007702000.zettel +++ docs/manual/00001007702000.zettel @@ -1,12 +1,11 @@ id: 00001007702000 title: Search term role: manual tags: #manual #search #zettelstore syntax: zmk -created: 20220805150154 -modified: 20230327123724 +modified: 20220821163727 A search term allows you to specify one search restriction. The result [[search expression|00001007700000]], which contains more than one search term, will be the applications of all restrictions. A search term can be one of the following (the first three term are collectively called __search literals__): @@ -30,17 +29,10 @@ Since search literals may be negated, it is possible to form any boolean search expression. Any search expression will be in a [[disjunctive normal form|https://en.wikipedia.org/wiki/Disjunctive_normal_form]]. It has no effect on the following search terms initiated with a special uppercase word. -* The string ''PICK'', followed by a non-empty sequence of spaces and a number greater zero (called ""N""). - - This will pick randomly N elements of the result list. - A zero value of N will produce the same result as if nothing was specified. - If specified multiple times, the lower value takes precedence. - - Example: ''PICK 5 PICK 3'' will be interpreted as ''PICK 3''. * The string ''ORDER'', followed by a non-empty sequence of spaces and the name of a metadata key, will specify an ordering of the result list. If you include the string ''REVERSE'' after ''ORDER'' but before the metadata key, the ordering will be reversed. Example: ''ORDER published'' will order the resulting list based on the publishing data, while ''ORDER REVERSED published'' will return a reversed result order. @@ -57,13 +49,13 @@ * The string ''RANDOM'' will provide a random order of the resulting list. Currently, only the first term specifying the order of the resulting list will be used. Other ordering terms will be ignored. - A random order specification will be ignored, if there is an explicit ordering given or if the specification contains a valid ''PICK''. + A random order specification will be ignored, if there is an explicit ordering given. - Example: ''RANDOM ORDER published'' will be interpreted as ''ORDER published'', ''PICK 3 RANDOM'' will be interpreted as ''PICK 3''. + Example: ''RANDOM ORDER published'' will be interpreted as ''ORDER published''. * The string ''OFFSET'', followed by a non-empty sequence of spaces and a number greater zero (called ""N""). This will ignore the first N elements of the result list, based on the specified sort order. A zero value of N will produce the same result as if nothing was specified. If specified multiple times, the higher value takes precedence. @@ -82,11 +74,6 @@ * A search term containing no [[search operator character|00001007705000]] is treated as a full-text search. * The first search operator character found in a search term divides the term into two pieces. If the first piece, from the beginning of the search term to the search operator character, is syntactically a metadata key, the search term is treated as a metadata-based search. * Otherwise, the search term is treated as a full-text search. -If a term like ''PICK'', ''ORDER'', ''ORDER REVERSE'', ''OFFSET'', or ''LIMIT'' is not followed by an appropriate value, it is interpreted as a search value for a full-text search. -For example, ''ORDER 123'' will search for a zettel conatining the strings ""ORDER"" (case-insensitive) and ""123"". - -A specification ''RANDOM LIMIT n'' (with some valid value of ''n'') will be interpreted as ''PICK n''. -Similar, a specification ''PICK n'', where ''n'' is greater or equal to the length of the result list, will be interpreted as ''RANDOM''. -A specification ''LIMIT n'', where ''n'' is greater or equal to the length of the result list, will be ignored effectively. +If a term like ''ORDER'', ''ORDER REVERSE'', ''OFFSET'', or ''LIMIT'' is not followed by an appropriate value, it is interpreted as a search value for a full-text search. For example, ''ORDER 123'' will search for a zettel conatining the strings ""ORDER"" (case-insensitive) and ""123"". Index: docs/manual/00001007780000.zettel ================================================================== --- docs/manual/00001007780000.zettel +++ docs/manual/00001007780000.zettel @@ -2,21 +2,20 @@ title: Formal syntax of query expressions role: manual tags: #manual #reference #search #zettelstore syntax: zmk created: 20220810144539 -modified: 20230327122634 +modified: 20220913134024 ``` QueryExpression := SearchExpression ActionExpression? SearchExpression := SearchTerm (SPACE+ SearchTerm)*. SearchTerm := SearchOperator? SearchValue | SearchKey SearchOperator SearchValue? | SearchKey ExistOperator | "OR" | "RANDOM" - | "PICK" SPACE+ PosInt | "ORDER" SPACE+ ("REVERSE" SPACE+)? SearchKey | "OFFSET" SPACE+ PosInt | "LIMIT" SPACE+ PosInt. SearchValue := Word. SearchKey := MetadataKey. Index: docs/manual/00001007790000.zettel ================================================================== --- docs/manual/00001007790000.zettel +++ docs/manual/00001007790000.zettel @@ -2,15 +2,15 @@ title: Useful query expressions role: manual tags: #example #manual #search #zettelstore syntax: zmk created: 20220810144539 -modified: 20230327122547 +modified: 20220917174956 |= Query Expression |= Meaning | [[query:role:configuration]] | Zettel that contains some configuration data for the Zettelstore | [[query:ORDER REVERSE created LIMIT 40]] | 40 recently created zettel | [[query:ORDER REVERSE published LIMIT 40]] | 40 recently updated zettel -| [[query:PICK 40]] | 40 random zettel +| [[query:RANDOM LIMIT 40]] | 40 random zettel | [[query:dead?]] | Zettel with invalid / dead links | [[query:backward!? precursor!?]] | Zettel that are not referenced by other zettel | [[query:tags!?]] | Zettel without tags Index: docs/manual/00001012051400.zettel ================================================================== --- docs/manual/00001012051400.zettel +++ docs/manual/00001012051400.zettel @@ -2,11 +2,11 @@ title: API: Query the list of all zettel role: manual tags: #api #manual #zettelstore syntax: zmk created: 20220912111111 -modified: 20230320151117 +modified: 20230116183844 The [[endpoint|00001012920000]] ''/z'' also allows you to filter the list of all zettel[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header] and optionally to provide some actions. A [[query|00001007700000]] is an optional [[search expression|00001007700000#search-expression]], together with an optional [[list of actions|00001007700000#action-list]] (described below). An empty search expression will select all zettel. @@ -45,11 +45,11 @@ Without a selection, the values are the empty string. ''"query"'' returns the normalized query expression itself, while ''"human"'' is the normalized query expression to be read by humans. An implicit precondition is that the zettel must contain the given metadata key. -For a metadata key like [[''title''|00001006020000#title]], which have a default value, this precondition should always be true. +For a metadata key like [[''title''|00001006020000#title]], which has a default value, this precondition should always be true. But the situation is different for a key like [[''url''|00001006020000#url]]. Both ``curl 'http://localhost:23123/z?q=url%3A'`` and ``curl 'http://localhost:23123/z?q=url%3A!'`` may result in an empty list. As an example for a query action, to list all roles used in the Zettelstore, send a HTTP GET request to the endpoint ''/z?q=|role''. Index: docs/manual/00001012053500.zettel ================================================================== --- docs/manual/00001012053500.zettel +++ docs/manual/00001012053500.zettel @@ -29,11 +29,11 @@ - + Index: docs/manual/00001012920500.zettel ================================================================== --- docs/manual/00001012920500.zettel +++ docs/manual/00001012920500.zettel @@ -2,15 +2,15 @@ title: Encodings available via the [[API|00001012000000]] role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20210126175322 -modified: 20230316180859 +modified: 20230109104839 A zettel representation can be encoded in various formats for further processing. * [[html|00001012920510]] * [[md|00001012920513]] * [[sexpr|00001012920516]] -* [[shtml|00001012920525]] * [[text|00001012920519]] +* [[zjson|00001012920503]] (will be deprecated in v0.11) * [[zmk|00001012920522]] ADDED docs/manual/00001012920503.zettel Index: docs/manual/00001012920503.zettel ================================================================== --- docs/manual/00001012920503.zettel +++ docs/manual/00001012920503.zettel @@ -0,0 +1,38 @@ +id: 00001012920503 +title: ZJSON Encoding +role: manual +tags: #api #manual #reference #zettelstore +syntax: zmk +created: 20210126175322 +modified: 20230109104722 + +**Note**: ZJSON encoding will be deprecated in v0.11 +--- +A zettel representation that allows to process the syntactic structure of a zettel. +It is a JSON-based encoding format, but different to the structures returned by using the plain [[endpoint|00001012920000]] ''/z/{ID}''. + +For an example, take a look at the ZJSON encoding of this page, which is available via the ""Info"" sub-page of this zettel: + +* [[//z/00001012920503?enc=zjson&part=zettel]], +* [[//z/00001012920503?enc=zjson&part=meta]], +* [[//z/00001012920503?enc=zjson&part=content]]. + +If transferred via HTTP, the content type will be ''application/json''. + +A full zettel encoding results in a JSON object with two keys: ''"meta"'' and ''"content"''. +Both values are the same as if you have requested just the appropriate [[part|00001012920800]]. + +=== Encoding of metadata +Metadata encoding results in a JSON object, where each metadata key is mapped to the same JSON object name. +The associated value is itself a JSON object with two names. +The first name ``""`` references the [[metadata key type|00001006030000]]. +Depending on the key type, the other name denotes the value of the metadata element. +The meaning of these names is [[well defined|00001012920582]], as well as the [[mapping of key types to used object names|00001012920584]]. + +=== Encoding of zettel content +The content encoding results in a JSON array of objects, where each objects represents a Zettelmarkup element. + +Every [!zettelmarkup|Zettelmarkup] element is encoded as a JSON object. +These objects always contain the empty name ''""'' with a string value describing the type of Zettelmarkup element. +Depending on the type, other one letter names denotes the details of the element. +The meaning of these names is [[well defined|00001012920588]]. Index: docs/manual/00001012920516.zettel ================================================================== --- docs/manual/00001012920516.zettel +++ docs/manual/00001012920516.zettel @@ -2,15 +2,17 @@ title: Sexpr Encoding role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20220422181104 -modified: 20230316182622 +modified: 20230109104812 A zettel representation that is a [[s-expression|https://en.wikipedia.org/wiki/S-expression]] (also known as symbolic expression). -It is (relatively) easy to parse and contain all relevant information of a zettel, metadata and content. +It is an alternative to the [[ZJSON encoding|00001012920503]][^ZJSON will be deprecated in v0.11]. +Both encodings are (relatively) easy to parse and contain all relevant information of a zettel, metadata and content. + For example, take a look at the Sexpr encoding of this page, which is available via the ""Info"" sub-page of this zettel: * [[//z/00001012920516?enc=sexpr&part=zettel]], * [[//z/00001012920516?enc=sexpr&part=meta]], * [[//z/00001012920516?enc=sexpr&part=content]]. @@ -20,12 +22,10 @@ === Syntax of s-expressions There are only two types of elements: atoms and lists. A list always starts with the left parenthesis (""''(''"", U+0028) and ends with a right parenthesis (""'')''"", U+0029). A list may contain a possibly empty sequence of elements, i.e. lists and / or atoms. -Before the last element of a list of at least to elements, a full stop character (""''.''"", U+002E) signal a pair as the last two elements. -This allows a more space economic storage of data. There are three syntactic forms for an atom: numbers, symbols and strings. A number is a non-empty sequence of digits (""0"" ... ""9""). The smallest number is ``0``, there are no negative numbers. DELETED docs/manual/00001012920525.zettel Index: docs/manual/00001012920525.zettel ================================================================== --- docs/manual/00001012920525.zettel +++ docs/manual/00001012920525.zettel @@ -1,36 +0,0 @@ -id: 00001012920525 -title: SHTML Encoding -role: manual -tags: #api #manual #reference #zettelstore -syntax: zmk -created: 20230316181044 -modified: 20230316182614 - -A zettel representation that is a [[s-expression|https://en.wikipedia.org/wiki/S-expression]], syntactically similar to the [[Sexpr encoding|00001012920516]], but denotes [[HTML|00001012920510]] semantics. -It is derived from a XML encoding in s-expressions, called [[SXML|https://en.wikipedia.org/wiki/SXML]]. - -It is (relatively) easy to parse and contains everything to transform it into real HTML. -In contrast to HTML, SHTML is easier to parse and to manipulate. -For example, take a look at the SHTML encoding of this page, which is available via the ""Info"" sub-page of this zettel: - -* [[//z/00001012920525?enc=shtml&part=zettel]], -* [[//z/00001012920525?enc=shtml&part=meta]], -* [[//z/00001012920525?enc=shtml&part=content]]. - -If transferred via HTTP, the content type will be ''text/plain''. - -Internally, if a zettel should be transformed into HTML, the zettel is translated into the [[Sexpr encoding|00001012920516]], which is transformed into this SHTML encoding to produce the [[HTML encoding|00001012920510]]. - -=== Syntax of SHTML -There are only two types of elements: atoms and lists, similar to the Sexpr encoding. - -A list always starts with the left parenthesis (""''(''"", U+0028) and ends with a right parenthesis (""'')''"", U+0029). -A list may contain a possibly empty sequence of elements, i.e. lists and / or atoms. -Before the last element of a list of at least to elements, a full stop character (""''.''"", U+002E) signal a pair as the last two elements. -This allows a more space economic storage of data. - -An HTML tag like ``< a href="link">Text`` is encoded in SHTML with a list, where the first element is a symbol named a the tag. -The second element is an optional encoding of the tag's attributes. -Further elements are either other tag encodings or a string. -The above tag is encoded as ``(a (@ (href . "link")) "Text")``. -Also possible is to encode the attribute without pairs: ``(a (@ (href "link")) "Text")`` (note the missing full stop character). ADDED docs/manual/00001012920582.zettel Index: docs/manual/00001012920582.zettel ================================================================== --- docs/manual/00001012920582.zettel +++ docs/manual/00001012920582.zettel @@ -0,0 +1,16 @@ +id: 00001012920582 +title: ZJSON Encoding: List of Valid Metadata Value Objects Names +role: manual +tags: #api #manual #reference #zettelstore +syntax: zmk +modified: 20230109104713 + +**Note**: ZJSON encoding will be deprecated in v0.11 +--- +Every Metadata value element is mapped to a JSON object with some well defined names / keys. + +|=Name | JSON Value | Meaning +| ''"\"'' | string | The type of the Zettelmarkup element. +| ''"i"'' | array | A sequence of [[inline-structured|00001007040000]] elements. +| ''"s"'' | string | The first / major string value of an element. +| ''"y"'' | array | A set of string values. ADDED docs/manual/00001012920584.zettel Index: docs/manual/00001012920584.zettel ================================================================== --- docs/manual/00001012920584.zettel +++ docs/manual/00001012920584.zettel @@ -0,0 +1,30 @@ +id: 00001012920584 +title: ZJSON Encoding: Mapping of Metadata Key Types to Object Names +role: manual +tags: #api #manual #reference #zettelstore +syntax: zmk +modified: 20230109104703 + +**Note**: ZJSON encoding will be deprecated in v0.11 +--- +Every [[Metadata key|00001006030000]] is mapped to an [[object name|00001012920582]] where its value is encoded. + +|=Type | JSON Object Name | Remark +| [[Credential|00001006031000]] | ''"s"'' | A string with the decrypted credential. +| [[EString|00001006031500]] | ''"s"'' | A possibly empty string. +| [[Identifier|00001006032000]] | ''"s"'' | A string containing a [[zettel identifier|00001006050000]]. +| [[IdentifierSet|00001006032500]] | ''"y"'' | An array of strings containing [[zettel identifier|00001006050000]]. +| [[Number|00001006033000]] | ''"s"'' | A string containing a numeric value. +| [[String|00001006033500]] | ''"s"'' | A non-empty string. +| [[TagSet|00001006034000]] | ''"y"'' | An array of string containing zettel tags. +| [[Timestamp|00001006034500]] | ''"s"'' | A string containing a timestamp in the format YYYYMMDDHHmmSS. +| [[URL|00001006035000]] | ''"s"'' | A string containing an URL. +| [[Word|00001006035500]] | ''"s"'' | A string containing a word (no space characters) +| [[WordSet|00001006036000]] | ''"y"'' | An array of strings containing words. +| [[Zettelmarkup|00001006036500]] | ''"i"'' | A sequence of [[inline-structured|00001007040000]] elements. + +Please note, that metadata is weakly typed. +Every metadata key expects a certain type. +But the user is free to enter something different. +For example, even if the metadata type is ""number"", its value could still be ""abc"". +However, the mapping itself is always valid. ADDED docs/manual/00001012920588.zettel Index: docs/manual/00001012920588.zettel ================================================================== --- docs/manual/00001012920588.zettel +++ docs/manual/00001012920588.zettel @@ -0,0 +1,27 @@ +id: 00001012920588 +title: ZJSON Encoding: List of Valid Zettelmarkup Element Objects Names +role: manual +tags: #api #manual #reference #zettelstore +syntax: zmk +modified: 20230109104648 + +**Note**: ZJSON encoding will be deprecated in v0.11 +--- + +Every [[Zettelmarkup|00001007000000]] element is mapped to a JSON object with some well defined names / keys. + +|=Name | JSON Value | Meaning +| ''"\"'' | string | The type of the Zettelmarkup element. +| ''"a"'' | object | Additional attributes of the element. +| ''"b"'' | array | A sequence of [[block-structured|00001007030000]] elements. +| ''"c"'' | array | A sequence of a sequence of (sub-) list elements or [[inline-structured|00001007040000]] elements. Used for nested lists. +| ''"d"'' | array | A sequence of description list elements, where each element is an object of a definition term and a list of descriptions. +| ''"e"'' | array | A sequence of descriptions: a JSON array of simple description, which is itself a JSON array of block structured elements. +| ''"i"'' | array | A sequence of [[inline-structured|00001007040000]] elements. +| ''"j"'' | object | An objects describing a BLOB element. +| ''"n"'' | number | A numeric value, e.g. for specifying the [[heading|00001007030300]] level. +| ''"o"'' | string | A base64 encoded binary value. Used in some BLOB elements. +| ''"p"'' | array | A sequence of two elements: a sequence of [[table|00001007031000]] header value, followed by a sequence of sequence of table row values. +| ''"q"'' | string | A second string value, if ''""s""'' is already used. +| ''"s"'' | string | The first / major string value of an element. +| ''"v"'' | string | A third string value, if ''""q""'' is already used. Index: domain/content.go ================================================================== --- domain/content.go +++ domain/content.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/content_test.go ================================================================== --- domain/content_test.go +++ domain/content_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/id/id.go ================================================================== --- domain/id/id.go +++ domain/id/id.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/id/id_test.go ================================================================== --- domain/id/id_test.go +++ domain/id/id_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/id/set.go ================================================================== --- domain/id/set.go +++ domain/id/set.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/id/set_test.go ================================================================== --- domain/id/set_test.go +++ domain/id/set_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -133,17 +133,17 @@ t.Errorf("%d: %v.Remove(%v) should be %v, but got %v", i, sl1, sl2, tc.exp, got) } } } -// func BenchmarkSet(b *testing.B) { -// s := id.Set{} -// for i := 0; i < b.N; i++ { -// s[id.Zid(i)] = true -// } -// } +// func BenchmarkSet(b *testing.B) { +// s := id.Set{} +// for i := 0; i < b.N; i++ { +// s[id.Zid(i)] = true +// } +// } func BenchmarkSet(b *testing.B) { s := id.Set{} for i := 0; i < b.N; i++ { s[id.Zid(i)] = struct{}{} } } Index: domain/id/slice.go ================================================================== --- domain/id/slice.go +++ domain/id/slice.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -9,12 +9,12 @@ //----------------------------------------------------------------------------- package id import ( + "bytes" "sort" - "strings" ) // Slice is a sequence of zettel identifier. A special case is a sorted slice. type Slice []Zid @@ -54,14 +54,14 @@ func (zs Slice) String() string { if len(zs) == 0 { return "" } - var sb strings.Builder + var buf bytes.Buffer for i, zid := range zs { if i > 0 { - sb.WriteByte(' ') + buf.WriteByte(' ') } - sb.WriteString(zid.String()) + buf.WriteString(zid.String()) } - return sb.String() + return buf.String() } Index: domain/id/slice_test.go ================================================================== --- domain/id/slice_test.go +++ domain/id/slice_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/collection.go ================================================================== --- domain/meta/collection.go +++ domain/meta/collection.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2022-present Detlef Stern +// Copyright (c) 2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/meta.go ================================================================== --- domain/meta/meta.go +++ domain/meta/meta.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -10,10 +10,11 @@ // Package meta provides the domain specific type 'meta'. package meta import ( + "bytes" "regexp" "sort" "strings" "unicode" "unicode/utf8" @@ -126,11 +127,11 @@ } // Supported keys. func init() { registerKey(api.KeyID, TypeID, usageComputed, "") - registerKey(api.KeyTitle, TypeEmpty, usageUser, "") + registerKey(api.KeyTitle, TypeZettelmarkup, usageUser, "") registerKey(api.KeyRole, TypeWord, usageUser, "") registerKey(api.KeyTags, TypeTagSet, usageUser, "") registerKey(api.KeySyntax, TypeWord, usageUser, "") // Properties that are inverse keys @@ -402,22 +403,22 @@ func RemoveNonGraphic(s string) string { if s == "" { return "" } pos := 0 - var sb strings.Builder + var buf bytes.Buffer for pos < len(s) { nextPos := strings.IndexFunc(s[pos:], func(r rune) bool { return !unicode.IsGraphic(r) }) if nextPos < 0 { break } - sb.WriteString(s[pos:nextPos]) - sb.WriteByte(' ') + buf.WriteString(s[pos:nextPos]) + buf.WriteByte(' ') _, size := utf8.DecodeRuneInString(s[nextPos:]) pos = nextPos + size } if pos == 0 { return strings.TrimSpace(s) } - sb.WriteString(s[pos:]) - return strings.TrimSpace(sb.String()) + buf.WriteString(s[pos:]) + return strings.TrimSpace(buf.String()) } Index: domain/meta/meta_test.go ================================================================== --- domain/meta/meta_test.go +++ domain/meta/meta_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/parse.go ================================================================== --- domain/meta/parse.go +++ domain/meta/parse.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/parse_test.go ================================================================== --- domain/meta/parse_test.go +++ domain/meta/parse_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/type.go ================================================================== --- domain/meta/type.go +++ domain/meta/type.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/type_test.go ================================================================== --- domain/meta/type_test.go +++ domain/meta/type_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/values.go ================================================================== --- domain/meta/values.go +++ domain/meta/values.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/write.go ================================================================== --- domain/meta/write.go +++ domain/meta/write.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: domain/meta/write_test.go ================================================================== --- domain/meta/write_test.go +++ domain/meta/write_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -9,10 +9,11 @@ //----------------------------------------------------------------------------- package meta_test import ( + "bytes" "strings" "testing" "zettelstore.de/c/api" "zettelstore.de/z/domain/id" @@ -34,13 +35,13 @@ } return m } func assertWriteMeta(t *testing.T, m *meta.Meta, expected string) { t.Helper() - var sb strings.Builder - m.Write(&sb) - if got := sb.String(); got != expected { + var buf bytes.Buffer + m.Write(&buf) + if got := buf.String(); got != expected { t.Errorf("\nExp: %q\ngot: %q", expected, got) } } func TestWriteMeta(t *testing.T) { Index: domain/zettel.go ================================================================== --- domain/zettel.go +++ domain/zettel.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: encoder/encoder.go ================================================================== --- encoder/encoder.go +++ encoder/encoder.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2020-present Detlef Stern +// Copyright (c) 2020-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations Index: encoder/encoder_blob_test.go ================================================================== --- encoder/encoder_blob_test.go +++ encoder/encoder_blob_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -38,13 +38,13 @@ 0x55, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, 0x36, 0x37, 0x7c, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, }, expect: expectMap{ + encoderZJSON: `[{"":"BLOB","q":[{"":"Text","s":"PNG"}],"s":"png","o":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="}]`, encoderHTML: `

PNG

`, - encoderSexpr: `(BLOCK (BLOB (INLINE (TEXT "PNG")) "png" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="))`, - encoderSHTML: `((p (img (@ (alt . "PNG") (src . "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg==")))))`, + encoderSexpr: `((BLOB ((TEXT "PNG")) "png" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="))`, encoderText: "", encoderZmk: `%% Unable to display BLOB with description 'PNG' and syntax 'png'.`, }, }, } Index: encoder/encoder_block_test.go ================================================================== --- encoder/encoder_block_test.go +++ encoder/encoder_block_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2022 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -13,182 +13,170 @@ var tcsBlock = []zmkTestCase{ { descr: "Empty Zettelmarkup should produce near nothing", zmk: "", expect: expectMap{ + encoderZJSON: `[]`, encoderHTML: "", encoderMD: "", - encoderSexpr: `(BLOCK)`, - encoderSHTML: `()`, + encoderSexpr: `()`, encoderText: "", encoderZmk: useZmk, }, }, { descr: "Simple text: Hello, world", zmk: "Hello, world", expect: expectMap{ + encoderZJSON: `[{"":"Para","i":[{"":"Text","s":"Hello,"},{"":"Space"},{"":"Text","s":"world"}]}]`, encoderHTML: "

Hello, world

", encoderMD: "Hello, world", - encoderSexpr: `(BLOCK (PARA (TEXT "Hello,") (SPACE) (TEXT "world")))`, - encoderSHTML: `((p "Hello," " " "world"))`, + encoderSexpr: `((PARA (TEXT "Hello,") (SPACE) (TEXT "world")))`, encoderText: "Hello, world", encoderZmk: useZmk, }, }, { descr: "Simple block comment", zmk: "%%%\nNo\nrender\n%%%", expect: expectMap{ + encoderZJSON: `[{"":"CommentBlock","s":"No\nrender"}]`, encoderHTML: ``, encoderMD: "", - encoderSexpr: `(BLOCK (VERBATIM-COMMENT () "No\nrender"))`, - encoderSHTML: `(())`, + encoderSexpr: `((VERBATIM-COMMENT () "No\nrender"))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "Rendered block comment", zmk: "%%%{-}\nRender\n%%%", expect: expectMap{ - encoderHTML: "\n", + encoderZJSON: `[{"":"CommentBlock","a":{"-":""},"s":"Render"}]`, + encoderHTML: "", encoderMD: "", - encoderSexpr: `(BLOCK (VERBATIM-COMMENT (quote (("-" . ""))) "Render"))`, - encoderSHTML: "((@@@ \"Render\"))", + encoderSexpr: `((VERBATIM-COMMENT (("-" "")) "Render"))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "Simple Heading", zmk: `=== Top`, expect: expectMap{ + encoderZJSON: `[{"":"Heading","n":1,"s":"top","i":[{"":"Text","s":"Top"}]}]`, encoderHTML: "

Top

", encoderMD: "# Top", - encoderSexpr: `(BLOCK (HEADING 1 () "top" "top" (INLINE (TEXT "Top"))))`, - encoderSHTML: `((h2 (@ (id . "top")) "Top"))`, + encoderSexpr: `((HEADING 1 () "top" "top" (TEXT "Top")))`, encoderText: `Top`, encoderZmk: useZmk, }, }, { descr: "Simple List", zmk: "* A\n* B\n* C", expect: expectMap{ + encoderZJSON: `[{"":"Bullet","c":[[{"":"Para","i":[{"":"Text","s":"A"}]}],[{"":"Para","i":[{"":"Text","s":"B"}]}],[{"":"Para","i":[{"":"Text","s":"C"}]}]]}]`, encoderHTML: "", encoderMD: "* A\n* B\n* C", - encoderSexpr: `(BLOCK (UNORDERED (INLINE (TEXT "A")) (INLINE (TEXT "B")) (INLINE (TEXT "C"))))`, - encoderSHTML: `((ul (li "A") (li "B") (li "C")))`, + encoderSexpr: `((UNORDERED ((TEXT "A")) ((TEXT "B")) ((TEXT "C"))))`, encoderText: "A\nB\nC", encoderZmk: useZmk, }, }, { descr: "Nested List", zmk: "* T1\n** T2\n* T3\n** T4\n** T5\n* T6", expect: expectMap{ + encoderZJSON: `[{"":"Bullet","c":[[{"":"Para","i":[{"":"Text","s":"T1"}]},{"":"Bullet","c":[[{"":"Para","i":[{"":"Text","s":"T2"}]}]]}],[{"":"Para","i":[{"":"Text","s":"T3"}]},{"":"Bullet","c":[[{"":"Para","i":[{"":"Text","s":"T4"}]}],[{"":"Para","i":[{"":"Text","s":"T5"}]}]]}],[{"":"Para","i":[{"":"Text","s":"T6"}]}]]}]`, encoderHTML: ``, encoderMD: "* T1\n * T2\n* T3\n * T4\n * T5\n* T6", - encoderSexpr: `(BLOCK (UNORDERED (BLOCK (PARA (TEXT "T1")) (UNORDERED (INLINE (TEXT "T2")))) (BLOCK (PARA (TEXT "T3")) (UNORDERED (INLINE (TEXT "T4")) (INLINE (TEXT "T5")))) (BLOCK (PARA (TEXT "T6")))))`, - encoderSHTML: `((ul (li (p "T1") (ul (li "T2"))) (li (p "T3") (ul (li "T4") (li "T5"))) (li (p "T6"))))`, + encoderSexpr: `((UNORDERED ((PARA (TEXT "T1")) (UNORDERED ((TEXT "T2")))) ((PARA (TEXT "T3")) (UNORDERED ((TEXT "T4")) ((TEXT "T5")))) ((PARA (TEXT "T6")))))`, encoderText: "T1\nT2\nT3\nT4\nT5\nT6", encoderZmk: useZmk, }, }, { descr: "Sequence of two lists", zmk: "* Item1.1\n* Item1.2\n* Item1.3\n\n* Item2.1\n* Item2.2", expect: expectMap{ + encoderZJSON: `[{"":"Bullet","c":[[{"":"Para","i":[{"":"Text","s":"Item1.1"}]}],[{"":"Para","i":[{"":"Text","s":"Item1.2"}]}],[{"":"Para","i":[{"":"Text","s":"Item1.3"}]}],[{"":"Para","i":[{"":"Text","s":"Item2.1"}]}],[{"":"Para","i":[{"":"Text","s":"Item2.2"}]}]]}]`, encoderHTML: "", encoderMD: "* Item1.1\n* Item1.2\n* Item1.3\n* Item2.1\n* Item2.2", - encoderSexpr: `(BLOCK (UNORDERED (INLINE (TEXT "Item1.1")) (INLINE (TEXT "Item1.2")) (INLINE (TEXT "Item1.3")) (INLINE (TEXT "Item2.1")) (INLINE (TEXT "Item2.2"))))`, - encoderSHTML: `((ul (li "Item1.1") (li "Item1.2") (li "Item1.3") (li "Item2.1") (li "Item2.2")))`, + encoderSexpr: `((UNORDERED ((TEXT "Item1.1")) ((TEXT "Item1.2")) ((TEXT "Item1.3")) ((TEXT "Item2.1")) ((TEXT "Item2.2"))))`, encoderText: "Item1.1\nItem1.2\nItem1.3\nItem2.1\nItem2.2", encoderZmk: "* Item1.1\n* Item1.2\n* Item1.3\n* Item2.1\n* Item2.2", }, }, { descr: "Simple horizontal rule", zmk: `---`, expect: expectMap{ + encoderZJSON: `[{"":"Thematic"}]`, encoderHTML: "
", encoderMD: "---", - encoderSexpr: `(BLOCK (THEMATIC ()))`, - encoderSHTML: `((hr))`, - encoderText: ``, - encoderZmk: useZmk, - }, - }, - { - descr: "Thematic break with attribute", - zmk: `---{lang="zmk"}`, - expect: expectMap{ - encoderHTML: `
`, - encoderMD: "---", - encoderSexpr: `(BLOCK (THEMATIC (quote (("lang" . "zmk")))))`, - encoderSHTML: `((hr (@ (lang . "zmk"))))`, + encoderSexpr: `((THEMATIC ()))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "No list after paragraph", zmk: "Text\n*abc", expect: expectMap{ + encoderZJSON: `[{"":"Para","i":[{"":"Text","s":"Text"},{"":"Soft"},{"":"Text","s":"*abc"}]}]`, encoderHTML: "

Text *abc

", encoderMD: "Text\n*abc", - encoderSexpr: `(BLOCK (PARA (TEXT "Text") (SOFT) (TEXT "*abc")))`, - encoderSHTML: `((p "Text" " " "*abc"))`, + encoderSexpr: `((PARA (TEXT "Text") (SOFT) (TEXT "*abc")))`, encoderText: `Text *abc`, encoderZmk: useZmk, }, }, { descr: "A list after paragraph", zmk: "Text\n# abc", expect: expectMap{ + encoderZJSON: `[{"":"Para","i":[{"":"Text","s":"Text"}]},{"":"Ordered","c":[[{"":"Para","i":[{"":"Text","s":"abc"}]}]]}]`, encoderHTML: "

Text

  1. abc
", encoderMD: "Text\n\n1. abc", - encoderSexpr: `(BLOCK (PARA (TEXT "Text")) (ORDERED (INLINE (TEXT "abc"))))`, - encoderSHTML: `((p "Text") (ol (li "abc")))`, + encoderSexpr: `((PARA (TEXT "Text")) (ORDERED ((TEXT "abc"))))`, encoderText: "Text\nabc", encoderZmk: useZmk, }, }, { descr: "Simple List Quote", zmk: "> ToBeOrNotToBe", expect: expectMap{ + encoderZJSON: `[{"":"Quotation","c":[[{"":"Para","i":[{"":"Text","s":"ToBeOrNotToBe"}]}]]}]`, encoderHTML: "

ToBeOrNotToBe

", encoderMD: "> ToBeOrNotToBe", - encoderSexpr: `(BLOCK (QUOTATION (INLINE (TEXT "ToBeOrNotToBe"))))`, - encoderSHTML: `((blockquote (p "ToBeOrNotToBe")))`, + encoderSexpr: `((QUOTATION ((TEXT "ToBeOrNotToBe"))))`, encoderText: "ToBeOrNotToBe", encoderZmk: useZmk, }, }, { descr: "Simple Quote Block", zmk: "<<<\nToBeOrNotToBe\n<<< Romeo", expect: expectMap{ + encoderZJSON: `[{"":"Excerpt","b":[{"":"Para","i":[{"":"Text","s":"ToBeOrNotToBe"}]}],"i":[{"":"Text","s":"Romeo"}]}]`, encoderHTML: "

ToBeOrNotToBe

Romeo
", encoderMD: "> ToBeOrNotToBe", - encoderSexpr: `(BLOCK (REGION-QUOTE () (BLOCK (PARA (TEXT "ToBeOrNotToBe"))) (INLINE (TEXT "Romeo"))))`, - encoderSHTML: `((blockquote (p "ToBeOrNotToBe") (cite "Romeo")))`, + encoderSexpr: `((REGION-QUOTE () ((PARA (TEXT "ToBeOrNotToBe"))) ((TEXT "Romeo"))))`, encoderText: "ToBeOrNotToBe\nRomeo", encoderZmk: useZmk, }, }, { descr: "Quote Block with multiple paragraphs", zmk: "<<<\nToBeOr\n\nNotToBe\n<<< Romeo", expect: expectMap{ + encoderZJSON: `[{"":"Excerpt","b":[{"":"Para","i":[{"":"Text","s":"ToBeOr"}]},{"":"Para","i":[{"":"Text","s":"NotToBe"}]}],"i":[{"":"Text","s":"Romeo"}]}]`, encoderHTML: "

ToBeOr

NotToBe

Romeo
", encoderMD: "> ToBeOr\n\n> NotToBe", - encoderSexpr: `(BLOCK (REGION-QUOTE () (BLOCK (PARA (TEXT "ToBeOr")) (PARA (TEXT "NotToBe"))) (INLINE (TEXT "Romeo"))))`, - encoderSHTML: `((blockquote (p "ToBeOr") (p "NotToBe") (cite "Romeo")))`, + encoderSexpr: `((REGION-QUOTE () ((PARA (TEXT "ToBeOr")) (PARA (TEXT "NotToBe"))) ((TEXT "Romeo"))))`, encoderText: "ToBeOr\nNotToBe\nRomeo", encoderZmk: useZmk, }, }, { @@ -201,14 +189,14 @@ Paragraph Spacy Para """ Author`, expect: expectMap{ + encoderZJSON: "[{\"\":\"Poem\",\"b\":[{\"\":\"Para\",\"i\":[{\"\":\"Text\",\"s\":\"A\"},{\"\":\"Space\",\"s\":\"\u00a0\"},{\"\":\"Text\",\"s\":\"line\"},{\"\":\"Hard\"},{\"\":\"Space\",\"s\":\"\u00a0\u00a0\"},{\"\":\"Text\",\"s\":\"another\"},{\"\":\"Space\",\"s\":\"\u00a0\"},{\"\":\"Text\",\"s\":\"line\"},{\"\":\"Hard\"},{\"\":\"Text\",\"s\":\"Back\"}]},{\"\":\"Para\",\"i\":[{\"\":\"Text\",\"s\":\"Paragraph\"}]},{\"\":\"Para\",\"i\":[{\"\":\"Space\",\"s\":\"\u00a0\u00a0\u00a0\u00a0\"},{\"\":\"Text\",\"s\":\"Spacy\"},{\"\":\"Space\",\"s\":\"\u00a0\u00a0\"},{\"\":\"Text\",\"s\":\"Para\"}]}],\"i\":[{\"\":\"Text\",\"s\":\"Author\"}]}]", encoderHTML: "

A\u00a0line
\u00a0\u00a0another\u00a0line
Back

Paragraph

\u00a0\u00a0\u00a0\u00a0Spacy\u00a0\u00a0Para

Author
", encoderMD: "", - encoderSexpr: "(BLOCK (REGION-VERSE () (BLOCK (PARA (TEXT \"A\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (SPACE \"\u00a0\u00a0\") (TEXT \"another\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (TEXT \"Back\")) (PARA (TEXT \"Paragraph\")) (PARA (SPACE \"\u00a0\u00a0\u00a0\u00a0\") (TEXT \"Spacy\") (SPACE \"\u00a0\u00a0\") (TEXT \"Para\"))) (INLINE (TEXT \"Author\"))))", - encoderSHTML: "((div (p \"A\" \"\u00a0\" \"line\" (br) \"\u00a0\u00a0\" \"another\" \"\u00a0\" \"line\" (br) \"Back\") (p \"Paragraph\") (p \"\u00a0\u00a0\u00a0\u00a0\" \"Spacy\" \"\u00a0\u00a0\" \"Para\") (cite \"Author\")))", + encoderSexpr: "((REGION-VERSE () ((PARA (TEXT \"A\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (SPACE \"\u00a0\u00a0\") (TEXT \"another\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (TEXT \"Back\")) (PARA (TEXT \"Paragraph\")) (PARA (SPACE \"\u00a0\u00a0\u00a0\u00a0\") (TEXT \"Spacy\") (SPACE \"\u00a0\u00a0\") (TEXT \"Para\"))) ((TEXT \"Author\"))))", encoderText: "A line\n another line\nBack\nParagraph\n Spacy Para\nAuthor", encoderZmk: "\"\"\"\nA\u00a0line\\\n\u00a0\u00a0another\u00a0line\\\nBack\nParagraph\n\u00a0\u00a0\u00a0\u00a0Spacy\u00a0\u00a0Para\n\"\"\" Author", }, }, { @@ -217,98 +205,86 @@ A simple span and much more :::`, expect: expectMap{ + encoderZJSON: `[{"":"Block","b":[{"":"Para","i":[{"":"Text","s":"A"},{"":"Space"},{"":"Text","s":"simple"},{"":"Soft"},{"":"Space"},{"":"Text","s":"span"},{"":"Soft"},{"":"Text","s":"and"},{"":"Space"},{"":"Text","s":"much"},{"":"Space"},{"":"Text","s":"more"}]}]}]`, encoderHTML: "

A simple span and much more

", encoderMD: "", - encoderSexpr: `(BLOCK (REGION-BLOCK () (BLOCK (PARA (TEXT "A") (SPACE) (TEXT "simple") (SOFT) (SPACE) (TEXT "span") (SOFT) (TEXT "and") (SPACE) (TEXT "much") (SPACE) (TEXT "more"))) (INLINE)))`, - encoderSHTML: `((div (p "A" " " "simple" " " " " "span" " " "and" " " "much" " " "more")))`, + encoderSexpr: `((REGION-BLOCK () ((PARA (TEXT "A") (SPACE) (TEXT "simple") (SOFT) (SPACE) (TEXT "span") (SOFT) (TEXT "and") (SPACE) (TEXT "much") (SPACE) (TEXT "more"))) ()))`, encoderText: `A simple span and much more`, encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Code", zmk: "```\nHello\nWorld\n```", expect: expectMap{ + encoderZJSON: `[{"":"CodeBlock","s":"Hello\nWorld"}]`, encoderHTML: "
Hello\nWorld
", encoderMD: " Hello\n World", - encoderSexpr: `(BLOCK (VERBATIM-CODE () "Hello\nWorld"))`, - encoderSHTML: `((pre (code "Hello\nWorld")))`, + encoderSexpr: `((VERBATIM-CODE () "Hello\nWorld"))`, encoderText: "Hello\nWorld", encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Code with visible spaces", zmk: "```{-}\nHello World\n```", expect: expectMap{ + encoderZJSON: `[{"":"CodeBlock","a":{"-":""},"s":"Hello World"}]`, encoderHTML: "
Hello\u2423World
", encoderMD: " Hello World", - encoderSexpr: `(BLOCK (VERBATIM-CODE (quote (("-" . ""))) "Hello World"))`, - encoderSHTML: "((pre (code \"Hello\u2423World\")))", + encoderSexpr: `((VERBATIM-CODE (("-" "")) "Hello World"))`, encoderText: "Hello World", encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Eval", zmk: "~~~\nHello\nWorld\n~~~", expect: expectMap{ + encoderZJSON: `[{"":"EvalBlock","s":"Hello\nWorld"}]`, encoderHTML: "
Hello\nWorld
", encoderMD: "", - encoderSexpr: `(BLOCK (VERBATIM-EVAL () "Hello\nWorld"))`, - encoderSHTML: "((pre (code (@ (class . \"zs-eval\")) \"Hello\\nWorld\")))", + encoderSexpr: `((VERBATIM-EVAL () "Hello\nWorld"))`, encoderText: "Hello\nWorld", encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Math", zmk: "$$$\nHello\n\\LaTeX\n$$$", expect: expectMap{ + encoderZJSON: `[{"":"MathBlock","s":"Hello\n\\LaTeX"}]`, encoderHTML: "
Hello\n\\LaTeX
", encoderMD: "", - encoderSexpr: `(BLOCK (VERBATIM-MATH () "Hello\n\\LaTeX"))`, - encoderSHTML: "((pre (code (@ (class . \"zs-math\")) \"Hello\\n\\\\LaTeX\")))", + encoderSexpr: `((VERBATIM-MATH () "Hello\n\\LaTeX"))`, encoderText: "Hello\n\\LaTeX", encoderZmk: useZmk, }, }, { descr: "Simple Description List", zmk: "; Zettel\n: Paper\n: Note\n; Zettelkasten\n: Slip box", expect: expectMap{ - encoderHTML: "
Zettel

Paper

Note

Zettelkasten

Slip box

", - encoderMD: "", - encoderSexpr: `(BLOCK (DESCRIPTION (INLINE (TEXT "Zettel")) (BLOCK (BLOCK (PARA (TEXT "Paper"))) (BLOCK (PARA (TEXT "Note")))) (INLINE (TEXT "Zettelkasten")) (BLOCK (BLOCK (PARA (TEXT "Slip") (SPACE) (TEXT "box"))))))`, - encoderSHTML: `((dl (dt "Zettel") (dd (p "Paper")) (dd (p "Note")) (dt "Zettelkasten") (dd (p "Slip" " " "box"))))`, - encoderText: "Zettel\nPaper\nNote\nZettelkasten\nSlip box", - encoderZmk: useZmk, - }, - }, - { - descr: "Description List with paragraphs as item", - zmk: "; Zettel\n: Paper\n\n Note\n; Zettelkasten\n: Slip box", - expect: expectMap{ - encoderHTML: "
Zettel

Paper

Note

Zettelkasten

Slip box

", - encoderMD: "", - encoderSexpr: `(BLOCK (DESCRIPTION (INLINE (TEXT "Zettel")) (BLOCK (BLOCK (PARA (TEXT "Paper")) (PARA (TEXT "Note")))) (INLINE (TEXT "Zettelkasten")) (BLOCK (BLOCK (PARA (TEXT "Slip") (SPACE) (TEXT "box"))))))`, - encoderSHTML: `((dl (dt "Zettel") (dd (p "Paper") (p "Note")) (dt "Zettelkasten") (dd (p "Slip" " " "box"))))`, + encoderZJSON: `[{"":"Description","d":[{"i":[{"":"Text","s":"Zettel"}],"e":[[{"":"Para","i":[{"":"Text","s":"Paper"}]}],[{"":"Para","i":[{"":"Text","s":"Note"}]}]]},{"i":[{"":"Text","s":"Zettelkasten"}],"e":[[{"":"Para","i":[{"":"Text","s":"Slip"},{"":"Space"},{"":"Text","s":"box"}]}]]}]}]`, + encoderHTML: "
Zettel
Paper
Note
Zettelkasten
Slip box
", + encoderMD: "", + encoderSexpr: `((DESCRIPTION ((TEXT "Zettel")) (((TEXT "Paper")) ((TEXT "Note"))) ((TEXT "Zettelkasten")) (((TEXT "Slip") (SPACE) (TEXT "box")))))`, encoderText: "Zettel\nPaper\nNote\nZettelkasten\nSlip box", encoderZmk: useZmk, }, }, { descr: "Simple Table", zmk: "|c1|c2|c3\n|d1||d3", expect: expectMap{ + encoderZJSON: `[{"":"Table","p":[[],[[{"i":[{"":"Text","s":"c1"}]},{"i":[{"":"Text","s":"c2"}]},{"i":[{"":"Text","s":"c3"}]}],[{"i":[{"":"Text","s":"d1"}]},{"i":[]},{"i":[{"":"Text","s":"d3"}]}]]]}]`, encoderHTML: `
c1c2c3
d1d3
`, encoderMD: "", - encoderSexpr: `(BLOCK (TABLE () (LIST (CELL (TEXT "c1")) (CELL (TEXT "c2")) (CELL (TEXT "c3"))) (LIST (CELL (TEXT "d1")) (CELL) (CELL (TEXT "d3")))))`, - encoderSHTML: `((table (tbody (tr (td "c1") (td "c2") (td "c3")) (tr (td "d1") (td) (td "d3")))))`, + encoderSexpr: `((TABLE () ((CELL (TEXT "c1")) (CELL (TEXT "c2")) (CELL (TEXT "c3"))) ((CELL (TEXT "d1")) (CELL) (CELL (TEXT "d3")))))`, encoderText: "c1 c2 c3\nd1 d3", encoderZmk: useZmk, }, }, { @@ -316,78 +292,66 @@ zmk: `|h1>|=h2|h3:| |%--+---+---+ |","i":[{"":"Text","s":"h1"}]},{"i":[{"":"Text","s":"h2"}]},{"s":":","i":[{"":"Text","s":"h3"}]}],[[{"s":"<","i":[{"":"Text","s":"c1"}]},{"i":[{"":"Text","s":"c2"}]},{"s":":","i":[{"":"Text","s":"c3"}]}],[{"s":">","i":[{"":"Text","s":"f1"}]},{"i":[{"":"Text","s":"f2"}]},{"s":":","i":[{"":"Text","s":"=f3"}]}]]]}]`, encoderHTML: `
h1h2h3
c1c2c3
f1f2=f3
`, encoderMD: "", - encoderSexpr: `(BLOCK (TABLE (LIST (CELL-RIGHT (TEXT "h1")) (CELL (TEXT "h2")) (CELL-CENTER (TEXT "h3"))) (LIST (CELL-LEFT (TEXT "c1")) (CELL (TEXT "c2")) (CELL-CENTER (TEXT "c3"))) (LIST (CELL-RIGHT (TEXT "f1")) (CELL (TEXT "f2")) (CELL-CENTER (TEXT "=f3")))))`, - encoderSHTML: `((table (thead (tr (td (@ (class . "right")) "h1") (td "h2") (td (@ (class . "center")) "h3"))) (tbody (tr (td (@ (class . "left")) "c1") (td "c2") (td (@ (class . "center")) "c3")) (tr (td (@ (class . "right")) "f1") (td "f2") (td (@ (class . "center")) "=f3")))))`, + encoderSexpr: `((TABLE ((CELL-RIGHT (TEXT "h1")) (CELL (TEXT "h2")) (CELL-CENTER (TEXT "h3"))) ((CELL-LEFT (TEXT "c1")) (CELL (TEXT "c2")) (CELL-CENTER (TEXT "c3"))) ((CELL-RIGHT (TEXT "f1")) (CELL (TEXT "f2")) (CELL-CENTER (TEXT "=f3")))))`, encoderText: "h1 h2 h3\nc1 c2 c3\nf1 f2 =f3", encoderZmk: `|=h1>|=h2|=h3: |Text1

  1. Endnote \u21a9\ufe0e
", - encoderMD: "Text", - encoderSexpr: `(BLOCK (PARA (TEXT "Text") (ENDNOTE () (quote (INLINE (TEXT "Endnote"))))))`, - encoderSHTML: "((p \"Text\" (sup (@ (id . \"fnref:1\")) (a (@ (class . \"zs-noteref\") (href . \"#fn:1\") (role . \"doc-noteref\")) \"1\"))))", - encoderText: "Text Endnote", - encoderZmk: useZmk, - }, - }, - { - descr: "Nested Endnotes", - zmk: `Text[^Endnote[^Nested]]`, - expect: expectMap{ - encoderHTML: "

Text1

  1. Endnote2 \u21a9\ufe0e
  2. Nested \u21a9\ufe0e
", - encoderMD: "Text", - encoderSexpr: `(BLOCK (PARA (TEXT "Text") (ENDNOTE () (quote (INLINE (TEXT "Endnote") (ENDNOTE () (quote (INLINE (TEXT "Nested")))))))))`, - encoderSHTML: "((p \"Text\" (sup (@ (id . \"fnref:1\")) (a (@ (class . \"zs-noteref\") (href . \"#fn:1\") (role . \"doc-noteref\")) \"1\"))))", - encoderText: "Text Endnote Nested", + descr: "Simple Endnotes", + zmk: `Text[^Footnote]`, + expect: expectMap{ + encoderZJSON: `[{"":"Para","i":[{"":"Text","s":"Text"},{"":"Footnote","i":[{"":"Text","s":"Footnote"}]}]}]`, + encoderHTML: `

Text1

  1. Footnote ↩︎
`, + encoderMD: "Text", + encoderSexpr: `((PARA (TEXT "Text") (FOOTNOTE () (TEXT "Footnote"))))`, + encoderText: "Text Footnote", encoderZmk: useZmk, }, }, { descr: "Transclusion", zmk: `{{{http://example.com/image}}}{width="100px"}`, expect: expectMap{ + encoderZJSON: `[{"":"Transclude","a":{"width":"100px"},"q":"external","s":"http://example.com/image"}]`, encoderHTML: `

`, encoderMD: "", - encoderSexpr: `(BLOCK (TRANSCLUDE (quote (("width" . "100px"))) (quote (EXTERNAL "http://example.com/image"))))`, - encoderSHTML: `((p (img (@ (class . "external") (src . "http://example.com/image") (width . "100px")))))`, + encoderSexpr: `((TRANSCLUDE (("width" "100px")) (EXTERNAL "http://example.com/image")))`, encoderText: "", encoderZmk: useZmk, }, }, { descr: "A paragraph with a inline comment only should be empty in HTML", zmk: `%% Comment`, expect: expectMap{ - // encoderHTML: ``, - encoderSexpr: `(BLOCK (PARA (LITERAL-COMMENT () "Comment")))`, - // encoderSHTML: ``, - encoderText: "", - encoderZmk: useZmk, + encoderZJSON: `[{"":"Para","i":[{"":"Comment","s":"Comment"}]}]`, + encoderHTML: ``, + encoderSexpr: `((PARA (LITERAL-COMMENT () "Comment")))`, + encoderText: "", + encoderZmk: useZmk, }, }, { descr: "", zmk: ``, expect: expectMap{ + encoderZJSON: `[]`, encoderHTML: ``, - encoderSexpr: `(BLOCK)`, - encoderSHTML: `()`, + encoderSexpr: `()`, encoderText: "", encoderZmk: useZmk, }, }, } // func TestEncoderBlock(t *testing.T) { // executeTestCases(t, tcsBlock) // } Index: encoder/encoder_inline_test.go ================================================================== --- encoder/encoder_inline_test.go +++ encoder/encoder_inline_test.go @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2021-present Detlef Stern +// Copyright (c) 2021-2023 Detlef Stern // // This file is part of Zettelstore. // // Zettelstore is licensed under the latest version of the EUPL (European Union // Public License). Please see file LICENSE.txt for your rights and obligations @@ -13,601 +13,542 @@ var tcsInline = []zmkTestCase{ { descr: "Empty Zettelmarkup should produce near nothing (inline)", zmk: "", expect: expectMap{ + encoderZJSON: `[]`, encoderHTML: "", encoderMD: "", - encoderSexpr: `(INLINE)`, - encoderSHTML: `()`, + encoderSexpr: `()`, encoderText: "", encoderZmk: useZmk, }, }, { descr: "Simple text: Hello, world (inline)", zmk: `Hello, world`, expect: expectMap{ + encoderZJSON: `[{"":"Text","s":"Hello,"},{"":"Space"},{"":"Text","s":"world"}]`, encoderHTML: "Hello, world", encoderMD: "Hello, world", - encoderSexpr: `(INLINE (TEXT "Hello,") (SPACE) (TEXT "world"))`, - encoderSHTML: `("Hello," " " "world")`, + encoderSexpr: `((TEXT "Hello,") (SPACE) (TEXT "world"))`, encoderText: "Hello, world", encoderZmk: useZmk, }, }, { - descr: "Soft Break", - zmk: "soft\nbreak", - expect: expectMap{ - encoderHTML: "soft break", - encoderMD: "soft\nbreak", - encoderSexpr: `(INLINE (TEXT "soft") (SOFT) (TEXT "break"))`, - encoderSHTML: `("soft" " " "break")`, - encoderText: "soft break", - encoderZmk: useZmk, - }, - }, - { - descr: "Hard Break", - zmk: "hard\\\nbreak", - expect: expectMap{ - encoderHTML: "hard
break", - encoderMD: "hard\\\nbreak", - encoderSexpr: `(INLINE (TEXT "hard") (HARD) (TEXT "break"))`, - encoderSHTML: `("hard" (br) "break")`, - encoderText: "hard\nbreak", - encoderZmk: useZmk, - }, - }, - { descr: "Emphasized formatting", zmk: "__emph__", expect: expectMap{ + encoderZJSON: `[{"":"Emph","i":[{"":"Text","s":"emph"}]}]`, encoderHTML: "emph", encoderMD: "*emph*", - encoderSexpr: `(INLINE (FORMAT-EMPH () (TEXT "emph")))`, - encoderSHTML: `((em "emph"))`, + encoderSexpr: `((FORMAT-EMPH () (TEXT "emph")))`, encoderText: "emph", encoderZmk: useZmk, }, }, { descr: "Strong formatting", zmk: "**strong**", expect: expectMap{ + encoderZJSON: `[{"":"Strong","i":[{"":"Text","s":"strong"}]}]`, encoderHTML: "strong", encoderMD: "__strong__", - encoderSexpr: `(INLINE (FORMAT-STRONG () (TEXT "strong")))`, - encoderSHTML: `((strong "strong"))`, + encoderSexpr: `((FORMAT-STRONG () (TEXT "strong")))`, encoderText: "strong", encoderZmk: useZmk, }, }, { descr: "Insert formatting", zmk: ">>insert>>", expect: expectMap{ + encoderZJSON: `[{"":"Insert","i":[{"":"Text","s":"insert"}]}]`, encoderHTML: "insert", encoderMD: "insert", - encoderSexpr: `(INLINE (FORMAT-INSERT () (TEXT "insert")))`, - encoderSHTML: `((ins "insert"))`, + encoderSexpr: `((FORMAT-INSERT () (TEXT "insert")))`, encoderText: "insert", encoderZmk: useZmk, }, }, { descr: "Delete formatting", zmk: "~~delete~~", expect: expectMap{ + encoderZJSON: `[{"":"Delete","i":[{"":"Text","s":"delete"}]}]`, encoderHTML: "delete", encoderMD: "delete", - encoderSexpr: `(INLINE (FORMAT-DELETE () (TEXT "delete")))`, - encoderSHTML: `((del "delete"))`, + encoderSexpr: `((FORMAT-DELETE () (TEXT "delete")))`, encoderText: "delete", encoderZmk: useZmk, }, }, { descr: "Update formatting", zmk: "~~old~~>>new>>", expect: expectMap{ + encoderZJSON: `[{"":"Delete","i":[{"":"Text","s":"old"}]},{"":"Insert","i":[{"":"Text","s":"new"}]}]`, encoderHTML: "oldnew", encoderMD: "oldnew", - encoderSexpr: `(INLINE (FORMAT-DELETE () (TEXT "old")) (FORMAT-INSERT () (TEXT "new")))`, - encoderSHTML: `((del "old") (ins "new"))`, + encoderSexpr: `((FORMAT-DELETE () (TEXT "old")) (FORMAT-INSERT () (TEXT "new")))`, encoderText: "oldnew", encoderZmk: useZmk, }, }, { descr: "Superscript formatting", zmk: "^^superscript^^", expect: expectMap{ + encoderZJSON: `[{"":"Super","i":[{"":"Text","s":"superscript"}]}]`, encoderHTML: `superscript`, encoderMD: "superscript", - encoderSexpr: `(INLINE (FORMAT-SUPER () (TEXT "superscript")))`, - encoderSHTML: `((sup "superscript"))`, + encoderSexpr: `((FORMAT-SUPER () (TEXT "superscript")))`, encoderText: `superscript`, encoderZmk: useZmk, }, }, { descr: "Subscript formatting", zmk: ",,subscript,,", expect: expectMap{ + encoderZJSON: `[{"":"Sub","i":[{"":"Text","s":"subscript"}]}]`, encoderHTML: `subscript`, encoderMD: "subscript", - encoderSexpr: `(INLINE (FORMAT-SUB () (TEXT "subscript")))`, - encoderSHTML: `((sub "subscript"))`, + encoderSexpr: `((FORMAT-SUB () (TEXT "subscript")))`, encoderText: `subscript`, encoderZmk: useZmk, }, }, { descr: "Quotes formatting", zmk: `""quotes""`, expect: expectMap{ + encoderZJSON: `[{"":"Quote","i":[{"":"Text","s":"quotes"}]}]`, encoderHTML: "quotes", encoderMD: "quotes", - encoderSexpr: `(INLINE (FORMAT-QUOTE () (TEXT "quotes")))`, - encoderSHTML: `((q "quotes"))`, + encoderSexpr: `((FORMAT-QUOTE () (TEXT "quotes")))`, encoderText: `quotes`, encoderZmk: useZmk, }, }, { descr: "Quotes formatting (german)", zmk: `""quotes""{lang=de}`, expect: expectMap{ + encoderZJSON: `[{"":"Quote","a":{"lang":"de"},"i":[{"":"Text","s":"quotes"}]}]`, encoderHTML: `quotes`, encoderMD: "quotes", - encoderSexpr: `(INLINE (FORMAT-QUOTE (quote (("lang" . "de"))) (TEXT "quotes")))`, - encoderSHTML: `((span (@ (lang . "de")) (q "quotes")))`, + encoderSexpr: `((FORMAT-QUOTE (("lang" "de")) (TEXT "quotes")))`, encoderText: `quotes`, encoderZmk: `""quotes""{lang="de"}`, }, }, { descr: "Span formatting", zmk: `::span::`, expect: expectMap{ + encoderZJSON: `[{"":"Span","i":[{"":"Text","s":"span"}]}]`, encoderHTML: `span`, encoderMD: "span", - encoderSexpr: `(INLINE (FORMAT-SPAN () (TEXT "span")))`, - encoderSHTML: `((span "span"))`, + encoderSexpr: `((FORMAT-SPAN () (TEXT "span")))`, encoderText: `span`, encoderZmk: useZmk, }, }, { descr: "Code formatting", zmk: "``code``", expect: expectMap{ + encoderZJSON: `[{"":"Code","s":"code"}]`, encoderHTML: `code`, encoderMD: "`code`", - encoderSexpr: `(INLINE (LITERAL-CODE () "code"))`, - encoderSHTML: `((code "code"))`, + encoderSexpr: `((LITERAL-CODE () "code"))`, encoderText: `code`, encoderZmk: useZmk, }, }, { descr: "Code formatting with visible space", zmk: "``x y``{-}", expect: expectMap{ + encoderZJSON: `[{"":"Code","a":{"-":""},"s":"x y"}]`, encoderHTML: "x\u2423y", encoderMD: "`x y`", - encoderSexpr: `(INLINE (LITERAL-CODE (quote (("-" . ""))) "x y"))`, - encoderSHTML: "((code \"x\u2423y\"))", + encoderSexpr: `((LITERAL-CODE (("-" "")) "x y"))`, encoderText: `x y`, encoderZmk: useZmk, }, }, { descr: "HTML in Code formatting", zmk: "``