Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From v0.17.0 To v0.16.0
2024-03-06
| ||
15:02 |
Increase version to 0.18.0-dev to begin next development cycle... (check-in: 51c141a192 user: stern tags: trunk) | |
2024-03-04
| ||
17:08 | Version 0.17.0 ... (check-in: c863ee5f61 user: stern tags: trunk, release, v0.17.0) | |
13:47 | Adapt to sx changes; add SPDX license identifiers ... (check-in: 5485ba3ce3 user: stern tags: trunk) | |
2023-12-28
| ||
16:41 | Fix sxn code that removed role-based customization, esp. for an additional action if role is "tag" ... (check-in: e721174596 user: t73fde tags: release-0.16) | |
2023-11-30
| ||
18:10 | Increase version to 0.17.0-dev to begin next development cycle ... (check-in: 9d654c5606 user: stern tags: trunk) | |
16:08 | Version 0.16.0 ... (check-in: a5afffaf5f user: stern tags: trunk, release, v0.16.0) | |
2023-11-29
| ||
16:53 | Add predefined role zettel ... (check-in: 514e400b28 user: stern tags: trunk) | |
Changes to .fossil-settings/ignore-glob.
1 2 | bin/* releases/* | > | 1 2 3 | bin/* releases/* parser/pikchr/*.out |
Changes to Makefile.
1 2 3 4 5 6 7 8 9 | ## Copyright (c) 2020-present 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 ## under this license. | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ## Copyright (c) 2020-present 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 ## under this license. .PHONY: check relcheck api build release clean check: go run tools/build.go check relcheck: go run tools/build.go relcheck api: go run tools/build.go testapi version: @echo $(shell go run tools/build.go version) build: go run tools/build.go build release: go run tools/build.go release clean: go run tools/build.go clean |
Changes to VERSION.
|
| | | 1 | 0.16.0 |
Changes to ast/ast.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package ast provides the abstract syntax tree for parsed zettel content. package ast import ( "net/url" |
︙ | ︙ |
Changes to ast/block.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package ast import "zettelstore.de/client.fossil/attrs" // Definition of Block nodes. |
︙ | ︙ |
Changes to ast/inline.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package ast import ( "unicode/utf8" |
︙ | ︙ |
Changes to ast/ref.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package ast import ( "net/url" "strings" "zettelstore.de/z/zettel/id" ) // QueryPrefix is the prefix that denotes a query expression. const QueryPrefix = "query:" // ParseReference parses a string and returns a reference. func ParseReference(s string) *Reference { if invalidReference(s) { return &Reference{URL: nil, Value: s, State: RefStateInvalid} } if strings.HasPrefix(s, QueryPrefix) { |
︙ | ︙ |
Changes to ast/ref_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package ast_test import ( "testing" |
︙ | ︙ |
Changes to ast/walk.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package ast // Visitor is a visitor for walking the AST. type Visitor interface { Visit(node Node) Visitor |
︙ | ︙ |
Changes to ast/walk_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package ast_test import ( "testing" |
︙ | ︙ | |||
60 61 62 63 64 65 66 | Ref: &ast.Reference{Value: "http://zettelstore.de"}, Inlines: ast.CreateInlineSliceFromWords("URL", "text."), }, ), } v := benchVisitor{} b.ResetTimer() | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | Ref: &ast.Reference{Value: "http://zettelstore.de"}, Inlines: ast.CreateInlineSliceFromWords("URL", "text."), }, ), } v := benchVisitor{} b.ResetTimer() for n := 0; n < b.N; n++ { ast.Walk(&v, &root) } } type benchVisitor struct{} func (bv *benchVisitor) Visit(ast.Node) ast.Visitor { return bv } |
Changes to auth/auth.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package auth provides services for authentification / authorization. package auth import ( "time" |
︙ | ︙ |
Changes to auth/cred/cred.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package cred provides some function for handling credentials. package cred import ( "bytes" |
︙ | ︙ |
Changes to auth/impl/digest.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "bytes" "crypto" "crypto/hmac" "encoding/base64" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxreader" ) var encoding = base64.RawURLEncoding const digestAlg = crypto.SHA384 func sign(claim sx.Object, secret []byte) ([]byte, error) { var buf bytes.Buffer sx.Print(&buf, claim) token := make([]byte, encoding.EncodedLen(buf.Len())) encoding.Encode(token, buf.Bytes()) digest := hmac.New(digestAlg.New, secret) _, err := digest.Write(buf.Bytes()) if err != nil { return nil, err } dig := digest.Sum(nil) encDig := make([]byte, encoding.EncodedLen(len(dig))) encoding.Encode(encDig, dig) |
︙ | ︙ |
Changes to auth/impl/impl.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package impl provides services for authentification / authorization. package impl import ( "errors" |
︙ | ︙ | |||
136 137 138 139 140 141 142 | return ErrTokenExpired } zid := id.Zid(vals[4].(sx.Int64)) if !zid.IsValid() { return ErrNoZid } | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | return ErrTokenExpired } zid := id.Zid(vals[4].(sx.Int64)) if !zid.IsValid() { return ErrNoZid } tokenData.Ident = ident.String() tokenData.Issued = issued tokenData.Now = now tokenData.Expires = expires tokenData.Zid = zid return nil } |
︙ | ︙ |
Changes to auth/policy/anon.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/z/auth" "zettelstore.de/z/config" |
︙ | ︙ |
Changes to auth/policy/box.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import ( "context" |
︙ | ︙ |
Changes to auth/policy/default.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" |
︙ | ︙ |
Changes to auth/policy/owner.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" |
︙ | ︙ |
Changes to auth/policy/policy.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package policy provides some interfaces and implementation for authorizsation policies. package policy import ( "zettelstore.de/z/auth" |
︙ | ︙ |
Changes to auth/policy/policy_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import ( "fmt" "testing" |
︙ | ︙ |
Changes to auth/policy/readonly.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package policy import "zettelstore.de/z/zettel/meta" type roPolicy struct{} |
︙ | ︙ |
Changes to box/box.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package box provides a generic interface to zettel boxes. package box import ( "context" |
︙ | ︙ |
Changes to box/compbox/compbox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package compbox provides zettel that have computed content. package compbox import ( "context" |
︙ | ︙ |
Changes to box/compbox/config.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "bytes" |
︙ | ︙ |
Changes to box/compbox/keys.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
︙ | ︙ |
Changes to box/compbox/log.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "bytes" |
︙ | ︙ |
Changes to box/compbox/manager.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
︙ | ︙ |
Changes to box/compbox/parser.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
︙ | ︙ |
Changes to box/compbox/version.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package compbox import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/kernel" |
︙ | ︙ |
Changes to box/constbox/base.css.
|
| < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 | *,*::before,*::after { box-sizing: border-box; } html { font-size: 1rem; font-family: serif; scroll-behavior: smooth; |
︙ | ︙ |
Changes to box/constbox/base.sxn.
|
| < < < < < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | `(@@@@ (html ,@(if lang `((@ (lang ,lang)))) (head (meta (@ (charset "utf-8"))) (meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0"))) (meta (@ (name "generator") (content "Zettelstore"))) (meta (@ (name "format-detection") (content "telephone=no"))) ,@META-HEADER (link (@ (rel "stylesheet") (href ,css-base-url))) (link (@ (rel "stylesheet") (href ,css-user-url))) ,@(ROLE-DEFAULT-meta (current-environment)) (title ,title)) (body (nav (@ (class "zs-menu")) (a (@ (href ,home-url)) "Home") ,@(if with-auth `((div (@ (class "zs-dropdown")) (button "User") |
︙ | ︙ | |||
48 49 50 51 52 53 54 | ,@(if new-zettel-links `((div (@ (class "zs-dropdown")) (button "New") (nav (@ (class "zs-dropdown-content")) ,@(map wui-link new-zettel-links) ))) ) | | < < | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ,@(if new-zettel-links `((div (@ (class "zs-dropdown")) (button "New") (nav (@ (class "zs-dropdown-content")) ,@(map wui-link new-zettel-links) ))) ) (form (@ (action ,search-url)) (input (@ (type "text") (placeholder "Search..") (name ,query-key-query) (dir "auto")))) ) (main (@ (class "content")) ,DETAIL) ,@(if FOOTER `((footer (hr) ,@FOOTER))) ,@(if debug-mode '((div (b "WARNING: Debug mode is enabled. DO NOT USE IN PRODUCTION!")))) ))) |
Changes to box/constbox/constbox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package constbox puts zettel inside the executable. package constbox import ( "context" |
︙ | ︙ | |||
177 178 179 180 181 182 183 | zettel.NewContent(contentDependencies)}, id.BaseTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Base HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230510155100", | | | | | | | | | | < | | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | zettel.NewContent(contentDependencies)}, id.BaseTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Base HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230510155100", api.KeyModified: "20230827212200", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentBaseSxn)}, id.LoginTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Login Form HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20200804111624", api.KeyModified: "20230527144100", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentLoginSxn)}, id.ZettelTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Zettel HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230510155300", api.KeyModified: "20231126180500", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentZettelSxn)}, id.InfoTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Info HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20200804111624", api.KeyModified: "20231023152000", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentInfoSxn)}, id.FormTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Form HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20200804111624", api.KeyModified: "20230621132600", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentFormSxn)}, id.RenameTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Rename Form HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20200804111624", api.KeyModified: "20230707190246", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentRenameSxn)}, id.DeleteTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Delete HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20200804111624", api.KeyModified: "20230621133100", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentDeleteSxn)}, id.ListTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore List Zettel HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230704122100", api.KeyModified: "20231129112800", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentListZettelSxn)}, id.ErrorTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Error HTML Template", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20210305133215", api.KeyModified: "20230527224800", api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentErrorSxn)}, id.StartSxnZid: { constHeader{ api.KeyTitle: "Zettelstore Sxn Start Code", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230824160700", api.KeyVisibility: api.ValueVisibilityExpert, api.KeyPrecursor: string(api.ZidSxnBase), }, zettel.NewContent(contentStartCodeSxn)}, id.BaseSxnZid: { constHeader{ api.KeyTitle: "Zettelstore Sxn Base Code", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20230619132800", api.KeyModified: "20231012154500", api.KeyReadOnly: api.ValueTrue, api.KeyVisibility: api.ValueVisibilityExpert, api.KeyPrecursor: string(api.ZidSxnPrelude), }, zettel.NewContent(contentBaseCodeSxn)}, id.PreludeSxnZid: { constHeader{ api.KeyTitle: "Zettelstore Sxn Prelude", api.KeyRole: api.ValueRoleConfiguration, api.KeySyntax: meta.SyntaxSxn, api.KeyCreated: "20231006181700", api.KeyModified: "20231019140400", api.KeyReadOnly: api.ValueTrue, api.KeyVisibility: api.ValueVisibilityExpert, }, zettel.NewContent(contentPreludeSxn)}, id.MustParse(api.ZidBaseCSS): { constHeader{ api.KeyTitle: "Zettelstore Base CSS", |
︙ | ︙ |
Changes to box/constbox/delete.sxn.
|
| < < < < < < < < < < < < < | 1 2 3 4 5 6 7 | `(article (header (h1 "Delete Zettel " ,zid)) (p "Do you really want to delete this zettel?") ,@(if shadowed-box `((div (@ (class "zs-info")) (h2 "Information") (p "If you delete this zettel, the previously shadowed zettel from overlayed box " ,shadowed-box " becomes available.") |
︙ | ︙ |
Changes to box/constbox/error.sxn.
|
| < < < < < < < < < < < < < | 1 2 3 4 | `(article (header (h1 ,heading)) ,message ) |
Changes to box/constbox/form.sxn.
|
| < < < < < < < < < < < < < | < < < < | | < < | < < < < | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | `(article (header (h1 ,heading)) (form (@ (action ,form-action-url) (method "POST") (enctype "multipart/form-data")) (div (label (@ (for "zs-title")) "Title " (a (@ (title "Main heading of this zettel.")) (@H "ⓘ"))) (input (@ (class "zs-input") (type "text") (id "zs-title") (name "title") (placeholder "Title..") (value ,meta-title) (dir "auto") (autofocus)))) (div (label (@ (for "zs-role")) "Role " (a (@ (title "One word, without spaces, to set the main role of this zettel.")) (@H "ⓘ"))) (input (@ (class "zs-input") (type "text") (id "zs-role") (name "role") (placeholder "role..") (value ,meta-role) (dir "auto") ,@(if role-data '((list "zs-role-data"))) )) ,@(wui-datalist "zs-role-data" role-data) ) (div (label (@ (for "zs-tags")) "Tags " (a (@ (title "Tags must begin with an '#' sign. They are separated by spaces.")) (@H "ⓘ"))) (input (@ (class "zs-input") (type "text") (id "zs-tags") (name "tags") (placeholder "#tag") (value ,meta-tags) (dir "auto")))) (div (label (@ (for "zs-meta")) "Metadata " (a (@ (title "Other metadata for this zettel. Each line contains a key/value pair, separated by a colon ':'.")) (@H "ⓘ"))) (textarea (@ (class "zs-input") (id "zs-meta") (name "meta") (rows "4") (placeholder "metakey: metavalue") (dir "auto")) ,meta)) (div (label (@ (for "zs-syntax")) "Syntax " (a (@ (title "Syntax of zettel content below, one word. Typically 'zmk' (for zettelmarkup).")) (@H "ⓘ"))) (input (@ (class "zs-input") (type "text") (id "zs-syntax") (name "syntax") (placeholder "syntax..") (value ,meta-syntax) (dir "auto") ,@(if syntax-data '((list "zs-syntax-data"))) )) ,@(wui-datalist "zs-syntax-data" syntax-data) ) ,@(if (bound? 'content) `((div (label (@ (for "zs-content")) "Content " (a (@ (title "Content for this zettel, according to above syntax.")) (@H "ⓘ"))) (textarea (@ (class "zs-input zs-content") (id "zs-content") (name "content") (rows "20") (placeholder "Zettel content..") (dir "auto")) ,content) )) ) (div (input (@ (class "zs-primary") (type "submit") (value "Submit"))) (input (@ (class "zs-secondary") (type "submit") (value "Save") (formaction "?save"))) (input (@ (class "zs-upload") (type "file") (id "zs-file") (name "file"))) )) |
︙ | ︙ |
Changes to box/constbox/info.sxn.
|
| < < < < < < < < < < < < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | `(article (header (h1 "Information for Zettel " ,zid) (p (a (@ (href ,web-url)) "Web") (@H " · ") (a (@ (href ,context-url)) "Context") ,@(if (bound? 'edit-url) `((@H " · ") (a (@ (href ,edit-url)) "Edit"))) ,@(ROLE-DEFAULT-actions (current-environment)) ,@(if (bound? 'reindex-url) `((@H " · ") (a (@ (href ,reindex-url)) "Reindex"))) ,@(if (bound? 'rename-url) `((@H " · ") (a (@ (href ,rename-url)) "Rename"))) ,@(if (bound? 'delete-url) `((@H " · ") (a (@ (href ,delete-url)) "Delete"))) ) ) (h2 "Interpreted Metadata") (table ,@(map wui-table-row metadata)) (h2 "References") ,@(if local-links `((h3 "Local") (ul ,@(map wui-valid-link local-links)))) ,@(if query-links `((h3 "Queries") (ul ,@(map wui-item-link query-links)))) ,@(if ext-links `((h3 "External") (ul ,@(map wui-item-popup-link ext-links)))) (h3 "Unlinked") ,@unlinked-content (form |
︙ | ︙ |
Changes to box/constbox/listzettel.sxn.
|
| < < < < < < < < < < < < < | < < | < | < | | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | `(article (header (h1 ,heading)) (form (@ (action ,search-url)) (input (@ (class "zs-input") (type "text") (placeholder "Search..") (name ,query-key-query) (value ,query-value) (dir "auto")))) ,@(if (bound? 'tag-zettel) `((p (@ (class "zs-meta-zettel")) "Tag zettel: " ,@tag-zettel)) ) ,@(if (bound? 'create-tag-zettel) `((p (@ (class "zs-meta-zettel")) "Create tag zettel: " ,@create-tag-zettel)) ) ,@(if (bound? 'role-zettel) `((p (@ (class "zs-meta-zettel")) "Role zettel: " ,@role-zettel)) ) ,@(if (bound? 'create-role-zettel) `((p (@ (class "zs-meta-zettel")) "Create role zettel: " ,@create-role-zettel)) ) ,@content ,@endnotes (form (@ (action ,(if (bound? 'create-url) create-url))) "Other encodings: " (a (@ (href ,data-url)) "data") ", " (a (@ (href ,plain-url)) "plain") ,@(if (bound? 'create-url) `((input (@ (type "hidden") (name ,query-key-query) (value ,query-value))) (input (@ (type "hidden") (name ,query-key-seed) (value ,seed))) (input (@ (class "zs-primary") (type "submit") (value "Save As Zettel"))) ) ) ) ) |
Changes to box/constbox/login.sxn.
|
| < < < < < < < < < < < < < | 1 2 3 4 5 6 7 | `(article (header (h1 "Login")) ,@(if retry '((div (@ (class "zs-indication zs-error")) "Wrong user name / password. Try again."))) (form (@ (method "POST") (action "")) (div (label (@ (for "username")) "User name:") (input (@ (class "zs-input") (type "text") (id "username") (name "username") (placeholder "Your user name..") (autofocus)))) |
︙ | ︙ |
Changes to box/constbox/prelude.sxn.
1 2 3 4 5 6 7 8 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. | < < < | | > > > > < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. ;;;---------------------------------------------------------------------------- ;;; This zettel contains all sxn definitions that are independent of specific ;;; subsystems, such as WebUI, API, or other. It just contains generic code to ;;; be used elsewhere. ;; Constants NIL and T (defconst NIL ()) (defconst T 'T) ;; Function not (defun not (x) (if x NIL T)) (defconst not not) ;; defunconst macro to define functions that are bound as a constant. ;; ;; (defunconst NAME ARGS EXPR ...) (defmacro defunconst (name args . body) `(begin (defun ,name ,args ,@body) (defconst ,name ,name))) ;; let macro ;; ;; (let (BINDING ...) EXPR ...), where BINDING is a list of two elements ;; (SYMBOL EXPR) (defmacro let (bindings . body) `((lambda ,(map car bindings) ,@body) ,@(map cadr bindings))) ;; let* macro ;; ;; (let* (BINDING ...) EXPR ...), where SYMBOL may occur in later bindings. (defmacro let* (bindings . body) (if (null? bindings) `((lambda () ,@body)) `((lambda (,(caar bindings)) (let* ,(cdr bindings) ,@body)) ,(cadar bindings)))) ;; and macro ;; ;; (and EXPR ...) (defmacro and args (cond ((null? args) T) ((null? (cdr args)) (car args)) (T `(if ,(car args) (and ,@(cdr args)))))) |
︙ | ︙ |
Changes to box/constbox/rename.sxn.
|
| < < < < < < < < < < < < < | 1 2 3 4 5 6 7 | `(article (header (h1 "Rename Zettel " ,zid)) (p "Do you really want to rename this zettel?") ,@(if incoming `((div (@ (class "zs-warning")) (h2 "Warning!") (p "If you rename this zettel, incoming references from the following zettel will become invalid.") |
︙ | ︙ | |||
28 29 30 31 32 33 34 | (ul ,@(map wui-item useless)) )) ) (form (@ (method "POST")) (input (@ (type "hidden") (id "curzid") (name "curzid") (value ,zid))) (div (label (@ (for "newzid")) "New zettel id") | < < < | | 15 16 17 18 19 20 21 22 23 24 25 26 | (ul ,@(map wui-item useless)) )) ) (form (@ (method "POST")) (input (@ (type "hidden") (id "curzid") (name "curzid") (value ,zid))) (div (label (@ (for "newzid")) "New zettel id") (input (@ (class "zs-input") (type "text") (id "newzid") (name "newzid") (placeholder "ZID..") (value ,zid) (autofocus)))) (div (input (@ (class "zs-primary") (type "submit") (value "Rename")))) ) ,(wui-meta-desc metapairs) ) |
Changes to box/constbox/start.sxn.
1 2 3 4 5 6 7 8 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. ;;;---------------------------------------------------------------------------- ;;; This zettel is the start of the loading sequence for Sx code used in the ;;; Zettelstore. Via the precursor metadata, dependend zettel are evaluated ;;; before this zettel. You must always depend, directly or indirectly on the ;;; "Zettelstore Sxn Base Code" zettel. It provides the base definitions. |
Changes to box/constbox/wuicode.sxn.
1 2 3 4 5 6 7 8 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. | < < < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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 under this license. ;;;---------------------------------------------------------------------------- ;; Contains WebUI specific code, but not related to a specific template. ;; wui-list-item returns the argument as a HTML list item. (defunconst wui-item (s) `(li ,s)) ;; wui-table-row takes a pair and translates it into a HTML table row with ;; two columns. (defunconst wui-table-row (p) `(tr (td ,(car p)) (td ,(cdr p)))) ;; wui-valid-link translates a local link into a HTML link. A link is a pair ;; (valid . url). If valid is not truish, only the invalid url is returned. (defunconst wui-valid-link (l) (if (car l) `(li (a (@ (href ,(cdr l))) ,(cdr l))) `(li ,(cdr l)))) |
︙ | ︙ | |||
74 75 76 77 78 79 80 | ;; identifier. It is used in the base template to update the metadata of the ;; HTML page to include some role specific CSS code. ;; Referenced in function "ROLE-DEFAULT-meta". (defvar CSS-ROLE-map '()) ;; ROLE-DEFAULT-meta returns some metadata for the base template. Any role ;; specific code should include the returned list of this function. | | | | | | | | | | | | | | | < < < < < < < < < < < | | | | | < < < < < | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | ;; identifier. It is used in the base template to update the metadata of the ;; HTML page to include some role specific CSS code. ;; Referenced in function "ROLE-DEFAULT-meta". (defvar CSS-ROLE-map '()) ;; ROLE-DEFAULT-meta returns some metadata for the base template. Any role ;; specific code should include the returned list of this function. (defunconst ROLE-DEFAULT-meta (env) `(,@(let* ((meta-role (environment-lookup 'meta-role env)) (entry (assoc CSS-ROLE-map meta-role))) (if (pair? entry) `((link (@ (rel "stylesheet") (href ,(zid-content-path (cdr entry)))))) ) ) ) ) ;;; ACTION-SEPARATOR defines a HTML value that separates actions links. (defvar ACTION-SEPARATOR '(@H " · ")) ;;; ROLE-DEFAULT-actions returns the default text for actions. (defunconst ROLE-DEFAULT-actions (env) `(,@(let ((copy-url (environment-lookup 'copy-url env))) (if (defined? copy-url) `((@H " · ") (a (@ (href ,copy-url)) "Copy")))) ,@(let ((version-url (environment-lookup 'version-url env))) (if (defined? version-url) `((@H " · ") (a (@ (href ,version-url)) "Version")))) ,@(let ((child-url (environment-lookup 'child-url env))) (if (defined? child-url) `((@H " · ") (a (@ (href ,child-url)) "Child")))) ,@(let ((folge-url (environment-lookup 'folge-url env))) (if (defined? folge-url) `((@H " · ") (a (@ (href ,folge-url)) "Folge")))) ) ) ;;; ROLE-tag-actions returns an additional action "Zettel" for zettel with role "tag". (defunconst ROLE-tag-actions (env) `(,@(ROLE-DEFAULT-actions env) ,@(let ((title (environment-lookup 'title env))) (if (and (defined? title) title) `(,ACTION-SEPARATOR (a (@ (href ,(query->url (string-append "tags:" title)))) "Zettel")) ) ) ) ) ;;; ROLE-DEFAULT-heading returns the default text for headings, below the ;;; references of a zettel. In most cases it should be called from an ;;; overwriting function. (defunconst ROLE-DEFAULT-heading (env) `(,@(let ((meta-url (environment-lookup 'meta-url env))) (if (defined? meta-url) `((br) "URL: " ,(url-to-html meta-url)))) ,@(let ((meta-author (environment-lookup 'meta-author env))) (if (and (defined? meta-author) meta-author) `((br) "By " ,meta-author))) ) ) |
Changes to box/constbox/zettel.sxn.
|
| < < < < < < < < < < < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | `(article (header (h1 ,heading) (div (@ (class "zs-meta")) ,@(if (bound? 'edit-url) `((a (@ (href ,edit-url)) "Edit") (@H " · "))) ,zid (@H " · ") (a (@ (href ,info-url)) "Info") (@H " · ") "(" ,@(if (bound? 'role-url) `((a (@ (href ,role-url)) ,meta-role))) ,@(if (and (bound? 'folge-role-url) (bound? 'meta-folge-role)) `((@H " → ") (a (@ (href ,folge-role-url)) ,meta-folge-role))) ")" ,@(if tag-refs `((@H " · ") ,@tag-refs)) ,@(ROLE-DEFAULT-actions (current-environment)) ,@(if predecessor-refs `((br) "Predecessor: " ,predecessor-refs)) ,@(if precursor-refs `((br) "Precursor: " ,precursor-refs)) ,@(if superior-refs `((br) "Superior: " ,superior-refs)) ,@(ROLE-DEFAULT-heading (current-environment)) ) ) ,@content ,endnotes ,@(if (or folge-links subordinate-links back-links successor-links) `((nav ,@(if folge-links `((details (@ (,folge-open)) (summary "Folgezettel") (ul ,@(map wui-item-link folge-links))))) |
︙ | ︙ |
Changes to box/dirbox/dirbox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package dirbox provides a directory-based zettel box. package dirbox import ( "context" |
︙ | ︙ | |||
88 89 90 91 92 93 94 | _ notifyTypeSpec = iota dirNotifyAny dirNotifySimple dirNotifyFS ) func getDirSrvInfo(log *logger.Logger, notifyType string) notifyTypeSpec { | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | _ notifyTypeSpec = iota dirNotifyAny dirNotifySimple dirNotifyFS ) func getDirSrvInfo(log *logger.Logger, notifyType string) notifyTypeSpec { for count := 0; count < 2; count++ { switch notifyType { case kernel.BoxDirTypeNotify: return dirNotifyFS case kernel.BoxDirTypeSimple: return dirNotifySimple default: notifyType = kernel.Main.GetConfig(kernel.BoxService, kernel.BoxDefaultDirType).(string) |
︙ | ︙ | |||
150 151 152 153 154 155 156 | 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) | | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | 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++ { cc := make(chan fileCmd) go fileService(i, dp.log.Clone().Str("sub", "file").Uint("fn", uint64(i)).Child(), dp.dir, cc) dp.fCmds = append(dp.fCmds, cc) } var notifier notify.Notifier var err error switch dp.notifySpec { case dirNotifySimple: notifier, err = notify.NewSimpleDirNotifier(dp.log.Clone().Str("notify", "simple").Child(), dp.dir) default: notifier, err = notify.NewFSDirNotifier(dp.log.Clone().Str("notify", "fs").Child(), dp.dir) } if err != nil { 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, |
︙ | ︙ |
Changes to box/dirbox/dirbox_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package dirbox import "testing" func TestIsPrime(t *testing.T) { |
︙ | ︙ | |||
31 32 33 34 35 36 37 | if got != tc.exp { t.Errorf("isPrime(%d)=%v, but got %v", tc.n, tc.exp, got) } } } func TestMakePrime(t *testing.T) { | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | if got != tc.exp { t.Errorf("isPrime(%d)=%v, but got %v", tc.n, tc.exp, got) } } } func TestMakePrime(t *testing.T) { for i := uint32(0); i < 1500; i++ { np := makePrime(i) if np < i { t.Errorf("makePrime(%d) < %d", i, np) continue } if !isPrime(np) { t.Errorf("makePrime(%d) == %d is not prime", i, np) |
︙ | ︙ |
Changes to box/dirbox/service.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package dirbox import ( "context" "io" "os" "path/filepath" "time" "zettelstore.de/z/box/filebox" "zettelstore.de/z/box/notify" "zettelstore.de/z/input" "zettelstore.de/z/kernel" "zettelstore.de/z/logger" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) func fileService(i uint32, log *logger.Logger, dirPath string, cmds <-chan fileCmd) { // Something may panic. Ensure a running service. defer func() { if ri := recover(); ri != nil { kernel.Main.LogRecover("FileService", ri) go fileService(i, log, dirPath, cmds) } }() log.Trace().Uint("i", uint64(i)).Str("dirpath", dirPath).Msg("File service started") for cmd := range cmds { cmd.run(log, dirPath) } log.Trace().Uint("i", uint64(i)).Str("dirpath", dirPath).Msg("File service stopped") } type fileCmd interface { run(*logger.Logger, string) } const serviceTimeout = 5 * time.Second // must be shorter than the web servers timeout values for reading+writing. // COMMAND: srvGetMeta ---------------------------------------- // // Retrieves the meta data from a zettel. |
︙ | ︙ | |||
75 76 77 78 79 80 81 | rc chan<- resGetMeta } type resGetMeta struct { meta *meta.Meta err error } | | | > | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | rc chan<- resGetMeta } type resGetMeta struct { meta *meta.Meta err error } func (cmd *fileGetMeta) run(log *logger.Logger, dirPath string) { var m *meta.Meta var err error entry := cmd.entry zid := entry.Zid if metaName := entry.MetaName; metaName == "" { contentName := entry.ContentName contentExt := entry.ContentExt if contentName == "" || contentExt == "" { log.Panic().Zid(zid).Msg("No meta, no content in getMeta") } if entry.HasMetaInContent() { m, _, err = parseMetaContentFile(zid, filepath.Join(dirPath, contentName)) } else { m = filebox.CalcDefaultMeta(zid, contentExt) } } else { m, err = parseMetaFile(zid, filepath.Join(dirPath, metaName)) } |
︙ | ︙ | |||
127 128 129 130 131 132 133 | } type resGetMetaContent struct { meta *meta.Meta content []byte err error } | | | > | | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | } type resGetMetaContent struct { meta *meta.Meta content []byte err error } func (cmd *fileGetMetaContent) run(log *logger.Logger, dirPath string) { var m *meta.Meta var content []byte var err error entry := cmd.entry zid := entry.Zid contentName := entry.ContentName contentExt := entry.ContentExt contentPath := filepath.Join(dirPath, contentName) if metaName := entry.MetaName; metaName == "" { if contentName == "" || contentExt == "" { log.Panic().Zid(zid).Msg("No meta, no content in getMetaContent") } if entry.HasMetaInContent() { m, content, err = parseMetaContentFile(zid, contentPath) } else { m = filebox.CalcDefaultMeta(zid, contentExt) content, err = os.ReadFile(contentPath) } } else { m, err = parseMetaFile(zid, filepath.Join(dirPath, metaName)) |
︙ | ︙ | |||
186 187 188 189 190 191 192 | type fileSetZettel struct { entry *notify.DirEntry zettel zettel.Zettel rc chan<- resSetZettel } type resSetZettel = error | | < | < > | | | | | | | < | | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | type fileSetZettel struct { entry *notify.DirEntry zettel zettel.Zettel rc chan<- resSetZettel } type resSetZettel = error func (cmd *fileSetZettel) run(log *logger.Logger, dirPath string) { entry := cmd.entry zid := entry.Zid contentName := entry.ContentName m := cmd.zettel.Meta content := cmd.zettel.Content.AsBytes() metaName := entry.MetaName if metaName == "" { if contentName == "" { log.Panic().Zid(zid).Msg("No meta, no content in setZettel") } contentPath := filepath.Join(dirPath, contentName) if entry.HasMetaInContent() { err := writeZettelFile(contentPath, m, content) cmd.rc <- err return } err := writeFileContent(contentPath, content) cmd.rc <- err return } err := writeMetaFile(filepath.Join(dirPath, metaName), m) if err == nil && contentName != "" { err = writeFileContent(filepath.Join(dirPath, contentName), content) } cmd.rc <- err } func writeMetaFile(metaPath string, m *meta.Meta) error { |
︙ | ︙ | |||
237 238 239 240 241 242 243 | } func writeZettelFile(contentPath string, m *meta.Meta, content []byte) error { zettelFile, err := openFileWrite(contentPath) if err != nil { return err } | > | > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | } func writeZettelFile(contentPath string, m *meta.Meta, content []byte) error { zettelFile, err := openFileWrite(contentPath) if err != nil { return err } if err == nil { err = writeMetaHeader(zettelFile, m) } if err == nil { _, err = zettelFile.Write(content) } if err1 := zettelFile.Close(); err == nil { err = err1 } return err |
︙ | ︙ | |||
298 299 300 301 302 303 304 | type fileDeleteZettel struct { entry *notify.DirEntry rc chan<- resDeleteZettel } type resDeleteZettel = error | | | < < > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | type fileDeleteZettel struct { entry *notify.DirEntry rc chan<- resDeleteZettel } type resDeleteZettel = error func (cmd *fileDeleteZettel) run(log *logger.Logger, dirPath string) { var err error entry := cmd.entry contentName := entry.ContentName contentPath := filepath.Join(dirPath, contentName) if metaName := entry.MetaName; metaName == "" { if contentName == "" { log.Panic().Zid(entry.Zid).Msg("No meta, no content in getMetaContent") } err = os.Remove(contentPath) } else { if contentName != "" { err = os.Remove(contentPath) } err1 := os.Remove(filepath.Join(dirPath, metaName)) if err == nil { err = err1 |
︙ | ︙ |
Changes to box/filebox/filebox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package filebox provides boxes that are stored in a file. package filebox import ( "errors" |
︙ | ︙ |
Changes to box/filebox/zipbox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package filebox import ( "archive/zip" "context" "io" "strings" "zettelstore.de/z/box" "zettelstore.de/z/box/notify" "zettelstore.de/z/input" "zettelstore.de/z/logger" "zettelstore.de/z/query" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
︙ | ︙ | |||
100 101 102 103 104 105 106 | var m *meta.Meta var src []byte var inMeta bool contentName := entry.ContentName if metaName := entry.MetaName; metaName == "" { if contentName == "" { | | < | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | var m *meta.Meta var src []byte var inMeta bool contentName := entry.ContentName if metaName := entry.MetaName; metaName == "" { if contentName == "" { zb.log.Panic().Zid(zid).Msg("No meta, no content in zipBox.GetZettel") } src, err = readZipFileContent(reader, entry.ContentName) if err != nil { return zettel.Zettel{}, err } if entry.HasMetaInContent() { inp := input.NewInput(src) |
︙ | ︙ | |||
210 211 212 213 214 215 216 | func (zb *zipBox) readZipMeta(reader *zip.ReadCloser, zid id.Zid, entry *notify.DirEntry) (m *meta.Meta, err error) { var inMeta bool if metaName := entry.MetaName; metaName == "" { contentName := entry.ContentName contentExt := entry.ContentExt if contentName == "" || contentExt == "" { | | > | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | func (zb *zipBox) readZipMeta(reader *zip.ReadCloser, zid id.Zid, entry *notify.DirEntry) (m *meta.Meta, err error) { var inMeta bool if metaName := entry.MetaName; metaName == "" { contentName := entry.ContentName contentExt := entry.ContentExt if contentName == "" || contentExt == "" { zb.log.Panic().Zid(zid).Msg("No meta, no content in getMeta") } if entry.HasMetaInContent() { m, err = readZipMetaFile(reader, zid, contentName) } else { m = CalcDefaultMeta(zid, contentExt) } } else { m, err = readZipMetaFile(reader, zid, metaName) } |
︙ | ︙ |
Changes to box/helper.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package box import ( "net/url" "strconv" "time" "zettelstore.de/z/zettel/id" ) // GetNewZid calculates a new and unused zettel identifier, based on the current date and time. func GetNewZid(testZid func(id.Zid) (bool, error)) (id.Zid, error) { withSeconds := false for i := 0; i < 90; i++ { // Must be completed within 9 seconds (less than web/server.writeTimeout) zid := id.New(withSeconds) found, err := testZid(zid) if err != nil { return id.Invalid, err } if found { return zid, nil |
︙ | ︙ |
Changes to box/manager/anteroom.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "sync" |
︙ | ︙ |
Changes to box/manager/anteroom_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "testing" |
︙ | ︙ |
Changes to box/manager/box.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "context" "errors" |
︙ | ︙ | |||
29 30 31 32 33 34 35 | // 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 | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // 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 for i := 0; i < len(mgr.boxes)-2; i++ { if i > 0 { sb.WriteString(", ") } sb.WriteString(mgr.boxes[i].Location()) } return sb.String() } |
︙ | ︙ | |||
266 267 268 269 270 271 272 | } mgr.mgrMx.RLock() defer mgr.mgrMx.RUnlock() for i, p := range mgr.boxes { err := p.RenameZettel(ctx, curZid, newZid) var errZNF box.ErrZettelNotFound if err != nil && !errors.As(err, &errZNF) { | | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | } mgr.mgrMx.RLock() defer mgr.mgrMx.RUnlock() for i, p := range mgr.boxes { err := p.RenameZettel(ctx, curZid, newZid) var errZNF box.ErrZettelNotFound if err != nil && !errors.As(err, &errZNF) { for j := 0; j < i; j++ { mgr.boxes[j].RenameZettel(ctx, newZid, curZid) } return err } } mgr.idxRenameZettel(ctx, curZid, newZid) return nil |
︙ | ︙ |
Changes to box/manager/collect.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "strings" |
︙ | ︙ |
Changes to box/manager/enrich.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "context" "strconv" |
︙ | ︙ |
Changes to box/manager/indexer.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package manager import ( "context" "fmt" |
︙ | ︙ |
Changes to box/manager/manager.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package manager coordinates the various boxes and indexes of a Zettelstore. package manager import ( "context" "io" "net/url" "sync" "time" "zettelstore.de/client.fossil/maps" "zettelstore.de/z/auth" "zettelstore.de/z/box" "zettelstore.de/z/box/manager/memstore" "zettelstore.de/z/box/manager/store" "zettelstore.de/z/config" "zettelstore.de/z/kernel" "zettelstore.de/z/logger" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" |
︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 | // Register the encoder for later retrieval. func Register(scheme string, create createFunc) { if _, ok := registry[scheme]; ok { panic(scheme) } registry[scheme] = create } // Manager is a coordinating box. type Manager struct { mgrLog *logger.Logger stateMx sync.RWMutex state box.StartState mgrMx sync.RWMutex | > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | // Register the encoder for later retrieval. func Register(scheme string, create createFunc) { if _, ok := registry[scheme]; ok { panic(scheme) } registry[scheme] = create } // GetSchemes returns all registered scheme, ordered by scheme string. 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 |
︙ | ︙ | |||
134 135 136 137 138 139 140 | mgr := &Manager{ mgrLog: boxLog.Clone().Str("box", "manager").Child(), rtConfig: rtConfig, infos: make(chan box.UpdateInfo, len(boxURIs)*10), propertyKeys: propertyKeys, idxLog: boxLog.Clone().Str("box", "index").Child(), | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | mgr := &Manager{ mgrLog: boxLog.Clone().Str("box", "manager").Child(), rtConfig: rtConfig, infos: make(chan box.UpdateInfo, len(boxURIs)*10), propertyKeys: propertyKeys, idxLog: boxLog.Clone().Str("box", "index").Child(), idxStore: memstore.New(), idxAr: newAnteroomQueue(1000), idxReady: make(chan struct{}, 1), } cdata := ConnectData{Number: 1, Config: rtConfig, Enricher: mgr, Notify: mgr.infos} boxes := make([]box.ManagedBox, 0, len(boxURIs)+2) for _, uri := range boxURIs { p, err := Connect(uri, authManager, &cdata) |
︙ | ︙ | |||
165 166 167 168 169 170 171 | } cdata.Number++ boxes = append(boxes, constbox, compbox) mgr.boxes = boxes return mgr, nil } | < < < < | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | } cdata.Number++ boxes = append(boxes, constbox, compbox) mgr.boxes = boxes return mgr, nil } // RegisterObserver registers an observer that will be notified // if a zettel was found to be changed. func (mgr *Manager) RegisterObserver(f box.UpdateFunc) { if f != nil { mgr.mxObserver.Lock() mgr.observers = append(mgr.observers, f) mgr.mxObserver.Unlock() |
︙ | ︙ | |||
251 252 253 254 255 256 257 | case box.OnReady: return case box.OnReload: mgr.idxAr.Reset() case box.OnZettel: mgr.idxAr.EnqueueZettel(zid) default: | | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | 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: } } |
︙ | ︙ | |||
314 315 316 317 318 319 320 | func (mgr *Manager) waitBoxesAreStarted() { const waitTime = 10 * time.Millisecond const waitLoop = int(1 * time.Second / waitTime) for i := 1; !mgr.allBoxesStarted(); i++ { if i%waitLoop == 0 { if time.Duration(i)*waitTime > time.Minute { | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | func (mgr *Manager) waitBoxesAreStarted() { const waitTime = 10 * time.Millisecond const waitLoop = int(1 * time.Second / waitTime) for i := 1; !mgr.allBoxesStarted(); i++ { if i%waitLoop == 0 { if time.Duration(i)*waitTime > time.Minute { mgr.mgrLog.Warn().Msg("Waiting for more than one minute to start") } else { mgr.mgrLog.Trace().Msg("Wait for boxes to start") } } time.Sleep(waitTime) } } |
︙ | ︙ | |||
372 373 374 375 376 377 378 | // ReIndex data of the given zettel. func (mgr *Manager) ReIndex(_ context.Context, zid id.Zid) error { mgr.mgrLog.Debug().Msg("ReIndex") if mgr.State() != box.StartStateStarted { return box.ErrStopped } | | | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | // ReIndex data of the given zettel. func (mgr *Manager) ReIndex(_ context.Context, zid id.Zid) error { mgr.mgrLog.Debug().Msg("ReIndex") if mgr.State() != box.StartStateStarted { return box.ErrStopped } mgr.infos <- box.UpdateInfo{Reason: box.OnReload, Zid: zid} return nil } // ReadStats populates st with box statistics. func (mgr *Manager) ReadStats(st *box.Stats) { mgr.mgrLog.Debug().Msg("ReadStats") mgr.mgrMx.RLock() |
︙ | ︙ |
Deleted box/manager/mapstore/mapstore.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted box/manager/mapstore/refs.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted box/manager/mapstore/refs_test.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added box/manager/memstore/memstore.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package memstore stored the index in main memory. package memstore import ( "context" "fmt" "io" "sort" "strings" "sync" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/maps" "zettelstore.de/z/box" "zettelstore.de/z/box/manager/store" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) type bidiRefs struct { forward id.Slice backward id.Slice } type zettelData struct { meta *meta.Meta // a local copy of the metadata, without computed keys dead id.Slice // list of dead references in this zettel forward id.Slice // list of forward references in this zettel backward id.Slice // list of zettel that reference with zettel otherRefs map[string]bidiRefs words []string // list of words of this zettel urls []string // list of urls of this zettel } type stringRefs map[string]id.Slice type memStore struct { mx sync.RWMutex intern map[string]string // map to intern strings idx map[id.Zid]*zettelData dead map[id.Zid]id.Slice // map dead refs where they occur words stringRefs urls stringRefs // Stats mxStats sync.Mutex updates uint64 } // New returns a new memory-based index store. func New() store.Store { return &memStore{ intern: make(map[string]string, 1024), idx: make(map[id.Zid]*zettelData), dead: make(map[id.Zid]id.Slice), words: make(stringRefs), urls: make(stringRefs), } } func (ms *memStore) GetMeta(_ context.Context, zid id.Zid) (*meta.Meta, error) { ms.mx.RLock() defer ms.mx.RUnlock() if zi, found := ms.idx[zid]; found && zi.meta != nil { // zi.meta is nil, if zettel was referenced, but is not indexed yet. return zi.meta.Clone(), nil } return nil, box.ErrZettelNotFound{Zid: zid} } func (ms *memStore) Enrich(_ context.Context, m *meta.Meta) { if ms.doEnrich(m) { ms.mxStats.Lock() ms.updates++ ms.mxStats.Unlock() } } func (ms *memStore) doEnrich(m *meta.Meta) bool { ms.mx.RLock() defer ms.mx.RUnlock() zi, ok := ms.idx[m.Zid] if !ok { return false } var updated bool if len(zi.dead) > 0 { m.Set(api.KeyDead, zi.dead.String()) updated = true } back := removeOtherMetaRefs(m, zi.backward.Clone()) if len(zi.backward) > 0 { m.Set(api.KeyBackward, zi.backward.String()) updated = true } if len(zi.forward) > 0 { m.Set(api.KeyForward, zi.forward.String()) back = remRefs(back, zi.forward) updated = true } for k, refs := range zi.otherRefs { if len(refs.backward) > 0 { m.Set(k, refs.backward.String()) back = remRefs(back, refs.backward) updated = true } } if len(back) > 0 { m.Set(api.KeyBack, back.String()) updated = true } return updated } // SearchEqual returns all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchEqual(word string) id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := id.NewSet() if refs, ok := ms.words[word]; ok { result.CopySlice(refs) } if refs, ok := ms.urls[word]; ok { result.CopySlice(refs) } zid, err := id.Parse(word) if err != nil { return result } zi, ok := ms.idx[zid] if !ok { return result } addBackwardZids(result, zid, zi) return result } // SearchPrefix returns all zettel that have a word with the given prefix. // The prefix must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchPrefix(prefix string) id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(prefix, strings.HasPrefix) l := len(prefix) if l > 14 { return result } maxZid, err := id.Parse(prefix + "99999999999999"[:14-l]) if err != nil { return result } var minZid id.Zid if l < 14 && prefix == "0000000000000"[:l] { minZid = id.Zid(1) } else { minZid, err = id.Parse(prefix + "00000000000000"[:14-l]) if err != nil { return result } } for zid, zi := range ms.idx { if minZid <= zid && zid <= maxZid { addBackwardZids(result, zid, zi) } } return result } // SearchSuffix returns all zettel that have a word with the given suffix. // The suffix must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchSuffix(suffix string) id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(suffix, strings.HasSuffix) l := len(suffix) if l > 14 { return result } val, err := id.ParseUint(suffix) if err != nil { return result } modulo := uint64(1) for i := 0; i < l; i++ { modulo *= 10 } for zid, zi := range ms.idx { if uint64(zid)%modulo == val { addBackwardZids(result, zid, zi) } } return result } // SearchContains returns all zettel that contains the given string. // The string must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchContains(s string) id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(s, strings.Contains) if len(s) > 14 { return result } if _, err := id.ParseUint(s); err != nil { return result } for zid, zi := range ms.idx { if strings.Contains(zid.String(), s) { addBackwardZids(result, zid, zi) } } return result } func (ms *memStore) selectWithPred(s string, pred func(string, string) bool) id.Set { // Must only be called if ms.mx is read-locked! result := id.NewSet() for word, refs := range ms.words { if !pred(word, s) { continue } result.CopySlice(refs) } for u, refs := range ms.urls { if !pred(u, s) { continue } result.CopySlice(refs) } return result } func addBackwardZids(result id.Set, zid id.Zid, zi *zettelData) { // Must only be called if ms.mx is read-locked! result.Add(zid) result.CopySlice(zi.backward) for _, mref := range zi.otherRefs { result.CopySlice(mref.backward) } } func removeOtherMetaRefs(m *meta.Meta, back id.Slice) id.Slice { for _, p := range m.PairsRest() { switch meta.Type(p.Key) { case meta.TypeID: if zid, err := id.Parse(p.Value); err == nil { back = remRef(back, zid) } case meta.TypeIDSet: for _, val := range meta.ListFromValue(p.Value) { if zid, err := id.Parse(val); err == nil { back = remRef(back, zid) } } } } return back } func (ms *memStore) UpdateReferences(_ context.Context, zidx *store.ZettelIndex) id.Set { ms.mx.Lock() defer ms.mx.Unlock() m := ms.makeMeta(zidx) zi, ziExist := ms.idx[zidx.Zid] if !ziExist || zi == nil { zi = &zettelData{} ziExist = false } // Is this zettel an old dead reference mentioned in other zettel? var toCheck id.Set if refs, ok := ms.dead[zidx.Zid]; ok { // These must be checked later again toCheck = id.NewSet(refs...) delete(ms.dead, zidx.Zid) } zi.meta = m ms.updateDeadReferences(zidx, zi) ids := ms.updateForwardBackwardReferences(zidx, zi) toCheck = toCheck.Copy(ids) ids = ms.updateMetadataReferences(zidx, zi) toCheck = toCheck.Copy(ids) zi.words = updateStrings(zidx.Zid, ms.words, zi.words, zidx.GetWords()) zi.urls = updateStrings(zidx.Zid, ms.urls, zi.urls, zidx.GetUrls()) // Check if zi must be inserted into ms.idx if !ziExist { ms.idx[zidx.Zid] = zi } return toCheck } var internableKeys = map[string]bool{ api.KeyRole: true, api.KeySyntax: true, api.KeyFolgeRole: true, api.KeyLang: true, api.KeyReadOnly: true, } func isInternableValue(key string) bool { if internableKeys[key] { return true } return strings.HasSuffix(key, "-role") } func (ms *memStore) internString(s string) string { if is, found := ms.intern[s]; found { return is } ms.intern[s] = s return s } func (ms *memStore) makeMeta(zidx *store.ZettelIndex) *meta.Meta { origM := zidx.GetMeta() copyM := meta.New(origM.Zid) for _, p := range origM.Pairs() { key := ms.internString(p.Key) if isInternableValue(key) { copyM.Set(key, ms.internString(p.Value)) } else if key == api.KeyBoxNumber || !meta.IsComputed(key) { copyM.Set(key, p.Value) } } return copyM } func (ms *memStore) updateDeadReferences(zidx *store.ZettelIndex, zi *zettelData) { // Must only be called if ms.mx is write-locked! drefs := zidx.GetDeadRefs() newRefs, remRefs := refsDiff(drefs, zi.dead) zi.dead = drefs for _, ref := range remRefs { ms.dead[ref] = remRef(ms.dead[ref], zidx.Zid) } for _, ref := range newRefs { ms.dead[ref] = addRef(ms.dead[ref], zidx.Zid) } } func (ms *memStore) updateForwardBackwardReferences(zidx *store.ZettelIndex, zi *zettelData) id.Set { // Must only be called if ms.mx is write-locked! brefs := zidx.GetBackRefs() newRefs, remRefs := refsDiff(brefs, zi.forward) zi.forward = brefs var toCheck id.Set for _, ref := range remRefs { bzi := ms.getOrCreateEntry(ref) bzi.backward = remRef(bzi.backward, zidx.Zid) if bzi.meta == nil { toCheck = toCheck.Add(ref) } } for _, ref := range newRefs { bzi := ms.getOrCreateEntry(ref) bzi.backward = addRef(bzi.backward, zidx.Zid) if bzi.meta == nil { toCheck = toCheck.Add(ref) } } return toCheck } func (ms *memStore) updateMetadataReferences(zidx *store.ZettelIndex, zi *zettelData) id.Set { // Must only be called if ms.mx is write-locked! inverseRefs := zidx.GetInverseRefs() for key, mr := range zi.otherRefs { if _, ok := inverseRefs[key]; ok { continue } ms.removeInverseMeta(zidx.Zid, key, mr.forward) } if zi.otherRefs == nil { zi.otherRefs = make(map[string]bidiRefs) } var toCheck id.Set for key, mrefs := range inverseRefs { mr := zi.otherRefs[key] newRefs, remRefs := refsDiff(mrefs, mr.forward) mr.forward = mrefs zi.otherRefs[key] = mr for _, ref := range newRefs { bzi := ms.getOrCreateEntry(ref) if bzi.otherRefs == nil { bzi.otherRefs = make(map[string]bidiRefs) } bmr := bzi.otherRefs[key] bmr.backward = addRef(bmr.backward, zidx.Zid) bzi.otherRefs[key] = bmr if bzi.meta == nil { toCheck = toCheck.Add(ref) } } ms.removeInverseMeta(zidx.Zid, key, remRefs) } return toCheck } func updateStrings(zid id.Zid, srefs stringRefs, prev []string, next store.WordSet) []string { newWords, removeWords := next.Diff(prev) for _, word := range newWords { if refs, ok := srefs[word]; ok { srefs[word] = addRef(refs, zid) continue } srefs[word] = id.Slice{zid} } for _, word := range removeWords { refs, ok := srefs[word] if !ok { continue } refs2 := remRef(refs, zid) if len(refs2) == 0 { delete(srefs, word) continue } srefs[word] = refs2 } return next.Words() } func (ms *memStore) getOrCreateEntry(zid id.Zid) *zettelData { // Must only be called if ms.mx is write-locked! if zi, ok := ms.idx[zid]; ok { return zi } zi := &zettelData{} ms.idx[zid] = zi return zi } func (ms *memStore) RenameZettel(_ context.Context, curZid, newZid id.Zid) id.Set { ms.mx.Lock() defer ms.mx.Unlock() curZi, curFound := ms.idx[curZid] _, newFound := ms.idx[newZid] if !curFound || newFound { return nil } newZi := &zettelData{ meta: copyMeta(curZi.meta, newZid), dead: ms.copyDeadReferences(curZi.dead), forward: ms.copyForward(curZi.forward, newZid), backward: nil, // will be done through tocheck otherRefs: nil, // TODO: check if this will be done through toCheck words: copyStrings(ms.words, curZi.words, newZid), urls: copyStrings(ms.urls, curZi.urls, newZid), } ms.idx[newZid] = newZi toCheck := ms.doDeleteZettel(curZid) toCheck = toCheck.CopySlice(ms.dead[newZid]) delete(ms.dead, newZid) toCheck = toCheck.Add(newZid) // should update otherRefs return toCheck } func copyMeta(m *meta.Meta, newZid id.Zid) *meta.Meta { result := m.Clone() result.Zid = newZid return result } func (ms *memStore) copyDeadReferences(curDead id.Slice) id.Slice { // Must only be called if ms.mx is write-locked! if l := len(curDead); l > 0 { result := make(id.Slice, l) for i, ref := range curDead { result[i] = ref ms.dead[ref] = addRef(ms.dead[ref], ref) } return result } return nil } func (ms *memStore) copyForward(curForward id.Slice, newZid id.Zid) id.Slice { // Must only be called if ms.mx is write-locked! if l := len(curForward); l > 0 { result := make(id.Slice, l) for i, ref := range curForward { result[i] = ref if fzi, found := ms.idx[ref]; found { fzi.backward = addRef(fzi.backward, newZid) } } return result } return nil } func copyStrings(msStringMap stringRefs, curStrings []string, newZid id.Zid) []string { // Must only be called if ms.mx is write-locked! if l := len(curStrings); l > 0 { result := make([]string, l) for i, s := range curStrings { result[i] = s msStringMap[s] = addRef(msStringMap[s], newZid) } return result } return nil } func (ms *memStore) DeleteZettel(_ context.Context, zid id.Zid) id.Set { ms.mx.Lock() defer ms.mx.Unlock() return ms.doDeleteZettel(zid) } func (ms *memStore) doDeleteZettel(zid id.Zid) id.Set { // Must only be called if ms.mx is write-locked! zi, ok := ms.idx[zid] if !ok { return nil } ms.deleteDeadSources(zid, zi) toCheck := ms.deleteForwardBackward(zid, zi) for key, mrefs := range zi.otherRefs { ms.removeInverseMeta(zid, key, mrefs.forward) } deleteStrings(ms.words, zi.words, zid) deleteStrings(ms.urls, zi.urls, zid) delete(ms.idx, zid) return toCheck } func (ms *memStore) deleteDeadSources(zid id.Zid, zi *zettelData) { // Must only be called if ms.mx is write-locked! for _, ref := range zi.dead { if drefs, ok := ms.dead[ref]; ok { drefs = remRef(drefs, zid) if len(drefs) > 0 { ms.dead[ref] = drefs } else { delete(ms.dead, ref) } } } } func (ms *memStore) deleteForwardBackward(zid id.Zid, zi *zettelData) id.Set { // Must only be called if ms.mx is write-locked! for _, ref := range zi.forward { if fzi, ok := ms.idx[ref]; ok { fzi.backward = remRef(fzi.backward, zid) } } var toCheck id.Set for _, ref := range zi.backward { if bzi, ok := ms.idx[ref]; ok { bzi.forward = remRef(bzi.forward, zid) toCheck = toCheck.Add(ref) } } return toCheck } func (ms *memStore) removeInverseMeta(zid id.Zid, key string, forward id.Slice) { // Must only be called if ms.mx is write-locked! for _, ref := range forward { bzi, ok := ms.idx[ref] if !ok || bzi.otherRefs == nil { continue } bmr, ok := bzi.otherRefs[key] if !ok { continue } bmr.backward = remRef(bmr.backward, zid) if len(bmr.backward) > 0 || len(bmr.forward) > 0 { bzi.otherRefs[key] = bmr } else { delete(bzi.otherRefs, key) if len(bzi.otherRefs) == 0 { bzi.otherRefs = nil } } } } func deleteStrings(msStringMap stringRefs, curStrings []string, zid id.Zid) { // Must only be called if ms.mx is write-locked! for _, word := range curStrings { refs, ok := msStringMap[word] if !ok { continue } refs2 := remRef(refs, zid) if len(refs2) == 0 { delete(msStringMap, word) continue } msStringMap[word] = refs2 } } func (ms *memStore) ReadStats(st *store.Stats) { ms.mx.RLock() st.Zettel = len(ms.idx) st.Words = uint64(len(ms.words)) st.Urls = uint64(len(ms.urls)) ms.mx.RUnlock() ms.mxStats.Lock() st.Updates = ms.updates ms.mxStats.Unlock() } func (ms *memStore) Dump(w io.Writer) { ms.mx.RLock() defer ms.mx.RUnlock() io.WriteString(w, "=== Dump\n") ms.dumpIndex(w) ms.dumpDead(w) dumpStringRefs(w, "Words", "", "", ms.words) dumpStringRefs(w, "URLs", "[[", "]]", ms.urls) } func (ms *memStore) dumpIndex(w io.Writer) { if len(ms.idx) == 0 { return } io.WriteString(w, "==== Zettel Index\n") zids := make(id.Slice, 0, len(ms.idx)) for id := range ms.idx { zids = append(zids, id) } zids.Sort() for _, id := range zids { fmt.Fprintln(w, "=====", id) zi := ms.idx[id] if len(zi.dead) > 0 { fmt.Fprintln(w, "* Dead:", zi.dead) } dumpZids(w, "* Forward:", zi.forward) dumpZids(w, "* Backward:", zi.backward) for k, fb := range zi.otherRefs { fmt.Fprintln(w, "* Meta", k) dumpZids(w, "** Forward:", fb.forward) dumpZids(w, "** Backward:", fb.backward) } dumpStrings(w, "* Words", "", "", zi.words) dumpStrings(w, "* URLs", "[[", "]]", zi.urls) } } func (ms *memStore) dumpDead(w io.Writer) { if len(ms.dead) == 0 { return } fmt.Fprintf(w, "==== Dead References\n") zids := make(id.Slice, 0, len(ms.dead)) for id := range ms.dead { zids = append(zids, id) } zids.Sort() for _, id := range zids { fmt.Fprintln(w, ";", id) fmt.Fprintln(w, ":", ms.dead[id]) } } func dumpZids(w io.Writer, prefix string, zids id.Slice) { if len(zids) > 0 { io.WriteString(w, prefix) for _, zid := range zids { io.WriteString(w, " ") w.Write(zid.Bytes()) } fmt.Fprintln(w) } } func dumpStrings(w io.Writer, title, preString, postString string, slice []string) { if len(slice) > 0 { sl := make([]string, len(slice)) copy(sl, slice) sort.Strings(sl) fmt.Fprintln(w, title) for _, s := range sl { fmt.Fprintf(w, "** %s%s%s\n", preString, s, postString) } } } func dumpStringRefs(w io.Writer, title, preString, postString string, srefs stringRefs) { if len(srefs) == 0 { return } fmt.Fprintln(w, "====", title) for _, s := range maps.Keys(srefs) { fmt.Fprintf(w, "; %s%s%s\n", preString, s, postString) fmt.Fprintln(w, ":", srefs[s]) } } |
Added box/manager/memstore/refs.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package memstore import ( "slices" "zettelstore.de/z/zettel/id" ) func refsDiff(refsN, refsO id.Slice) (newRefs, remRefs id.Slice) { npos, opos := 0, 0 for npos < len(refsN) && opos < len(refsO) { rn, ro := refsN[npos], refsO[opos] if rn == ro { npos++ opos++ continue } if rn < ro { newRefs = append(newRefs, rn) npos++ continue } remRefs = append(remRefs, ro) opos++ } if npos < len(refsN) { newRefs = append(newRefs, refsN[npos:]...) } if opos < len(refsO) { remRefs = append(remRefs, refsO[opos:]...) } return newRefs, remRefs } func addRef(refs id.Slice, ref id.Zid) id.Slice { hi := len(refs) for lo := 0; lo < hi; { m := lo + (hi-lo)/2 if r := refs[m]; r == ref { return refs } else if r < ref { lo = m + 1 } else { hi = m } } refs = slices.Insert(refs, hi, ref) return refs } func remRefs(refs, rem id.Slice) id.Slice { if len(refs) == 0 || len(rem) == 0 { return refs } result := make(id.Slice, 0, len(refs)) rpos, dpos := 0, 0 for rpos < len(refs) && dpos < len(rem) { rr, dr := refs[rpos], rem[dpos] if rr < dr { result = append(result, rr) rpos++ continue } if dr < rr { dpos++ continue } rpos++ dpos++ } if rpos < len(refs) { result = append(result, refs[rpos:]...) } return result } func remRef(refs id.Slice, ref id.Zid) id.Slice { hi := len(refs) for lo := 0; lo < hi; { m := lo + (hi-lo)/2 if r := refs[m]; r == ref { copy(refs[m:], refs[m+1:]) refs = refs[:len(refs)-1] return refs } else if r < ref { lo = m + 1 } else { hi = m } } return refs } |
Added box/manager/memstore/refs_test.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package memstore import ( "testing" "zettelstore.de/z/zettel/id" ) func assertRefs(t *testing.T, i int, got, exp id.Slice) { t.Helper() if got == nil && exp != nil { t.Errorf("%d: got nil, but expected %v", i, exp) return } if got != nil && exp == nil { t.Errorf("%d: expected nil, but got %v", i, got) return } if len(got) != len(exp) { t.Errorf("%d: expected len(%v)==%d, but got len(%v)==%d", i, exp, len(exp), got, len(got)) return } for p, n := range exp { if got := got[p]; got != id.Zid(n) { t.Errorf("%d: pos %d: expected %d, but got %d", i, p, n, got) } } } func TestRefsDiff(t *testing.T) { t.Parallel() testcases := []struct { in1, in2 id.Slice exp1, exp2 id.Slice }{ {nil, nil, nil, nil}, {id.Slice{1}, nil, id.Slice{1}, nil}, {nil, id.Slice{1}, nil, id.Slice{1}}, {id.Slice{1}, id.Slice{1}, nil, nil}, {id.Slice{1, 2}, id.Slice{1}, id.Slice{2}, nil}, {id.Slice{1, 2}, id.Slice{1, 3}, id.Slice{2}, id.Slice{3}}, {id.Slice{1, 4}, id.Slice{1, 3}, id.Slice{4}, id.Slice{3}}, } for i, tc := range testcases { got1, got2 := refsDiff(tc.in1, tc.in2) assertRefs(t, i, got1, tc.exp1) assertRefs(t, i, got2, tc.exp2) } } func TestAddRef(t *testing.T) { t.Parallel() testcases := []struct { ref id.Slice zid uint exp id.Slice }{ {nil, 5, id.Slice{5}}, {id.Slice{1}, 5, id.Slice{1, 5}}, {id.Slice{10}, 5, id.Slice{5, 10}}, {id.Slice{5}, 5, id.Slice{5}}, {id.Slice{1, 10}, 5, id.Slice{1, 5, 10}}, {id.Slice{1, 5, 10}, 5, id.Slice{1, 5, 10}}, } for i, tc := range testcases { got := addRef(tc.ref, id.Zid(tc.zid)) assertRefs(t, i, got, tc.exp) } } func TestRemRefs(t *testing.T) { t.Parallel() testcases := []struct { in1, in2 id.Slice exp id.Slice }{ {nil, nil, nil}, {nil, id.Slice{}, nil}, {id.Slice{}, nil, id.Slice{}}, {id.Slice{}, id.Slice{}, id.Slice{}}, {id.Slice{1}, id.Slice{5}, id.Slice{1}}, {id.Slice{10}, id.Slice{5}, id.Slice{10}}, {id.Slice{1, 5}, id.Slice{5}, id.Slice{1}}, {id.Slice{5, 10}, id.Slice{5}, id.Slice{10}}, {id.Slice{1, 10}, id.Slice{5}, id.Slice{1, 10}}, {id.Slice{1}, id.Slice{2, 5}, id.Slice{1}}, {id.Slice{10}, id.Slice{2, 5}, id.Slice{10}}, {id.Slice{1, 5}, id.Slice{2, 5}, id.Slice{1}}, {id.Slice{5, 10}, id.Slice{2, 5}, id.Slice{10}}, {id.Slice{1, 2, 5}, id.Slice{2, 5}, id.Slice{1}}, {id.Slice{2, 5, 10}, id.Slice{2, 5}, id.Slice{10}}, {id.Slice{1, 10}, id.Slice{2, 5}, id.Slice{1, 10}}, {id.Slice{1}, id.Slice{5, 9}, id.Slice{1}}, {id.Slice{10}, id.Slice{5, 9}, id.Slice{10}}, {id.Slice{1, 5}, id.Slice{5, 9}, id.Slice{1}}, {id.Slice{5, 10}, id.Slice{5, 9}, id.Slice{10}}, {id.Slice{1, 5, 9}, id.Slice{5, 9}, id.Slice{1}}, {id.Slice{5, 9, 10}, id.Slice{5, 9}, id.Slice{10}}, {id.Slice{1, 10}, id.Slice{5, 9}, id.Slice{1, 10}}, } for i, tc := range testcases { got := remRefs(tc.in1, tc.in2) assertRefs(t, i, got, tc.exp) } } func TestRemRef(t *testing.T) { t.Parallel() testcases := []struct { ref id.Slice zid uint exp id.Slice }{ {nil, 5, nil}, {id.Slice{}, 5, id.Slice{}}, {id.Slice{5}, 5, id.Slice{}}, {id.Slice{1}, 5, id.Slice{1}}, {id.Slice{10}, 5, id.Slice{10}}, {id.Slice{1, 5}, 5, id.Slice{1}}, {id.Slice{5, 10}, 5, id.Slice{10}}, {id.Slice{1, 5, 10}, 5, id.Slice{1, 10}}, } for i, tc := range testcases { got := remRef(tc.ref, id.Zid(tc.zid)) assertRefs(t, i, got, tc.exp) } } |
Changes to box/manager/store/store.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package store contains general index data for storing a zettel index. package store import ( "context" |
︙ | ︙ |
Changes to box/manager/store/wordset.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package store // WordSet contains the set of all words, with the count of their occurrences. type WordSet map[string]int |
︙ | ︙ |
Changes to box/manager/store/wordset_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package store_test import ( "sort" "testing" |
︙ | ︙ |
Changes to box/manager/store/zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package store import ( "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" |
︙ | ︙ |
Changes to box/membox/membox.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package membox stores zettel volatile in main memory. package membox import ( "context" |
︙ | ︙ |
Changes to box/notify/directory.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "errors" "fmt" |
︙ | ︙ | |||
256 257 258 259 260 261 262 | return nil, false } switch ev.Op { case Error: newEntries = nil if state != DsMissing { | | | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | return nil, false } switch ev.Op { case Error: newEntries = nil 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() |
︙ | ︙ | |||
297 298 299 300 301 302 303 | ds.mx.Lock() zid := ds.onDeleteFileEvent(ds.entries, ev.Name) ds.mx.Unlock() if zid != id.Invalid { ds.notifyChange(zid) } default: | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | ds.mx.Lock() zid := ds.onDeleteFileEvent(ds.entries, ev.Name) ds.mx.Unlock() if zid != id.Invalid { ds.notifyChange(zid) } default: ds.log.Warn().Str("event", fmt.Sprintf("%v", ev)).Msg("Unknown zettel notification event") } return newEntries, true } func getNewZids(entries entrySet) id.Slice { zids := make(id.Slice, 0, len(entries)) for zid := range entries { |
︙ | ︙ | |||
372 373 374 375 376 377 378 | zid := seekZid(name) if zid == id.Invalid { return id.Invalid } entry := fetchdirEntry(entries, zid) dupName1, dupName2 := ds.updateEntry(entry, name) if dupName1 != "" { | | | | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | zid := seekZid(name) if zid == id.Invalid { return id.Invalid } entry := fetchdirEntry(entries, zid) dupName1, dupName2 := ds.updateEntry(entry, name) if dupName1 != "" { ds.log.Warn().Str("name", dupName1).Msg("Duplicate content (is ignored)") if dupName2 != "" { ds.log.Warn().Str("name", dupName2).Msg("Duplicate content (is ignored)") } return id.Invalid } return zid } func (ds *DirService) onDeleteFileEvent(entries entrySet, name string) id.Zid { |
︙ | ︙ |
Changes to box/notify/directory_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "testing" |
︙ | ︙ |
Changes to box/notify/entry.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "path/filepath" |
︙ | ︙ | |||
58 59 60 61 62 63 64 | if contentName := e.ContentName; contentName != "" { if !extIsMetaAndContent(e.ContentExt) && e.MetaName == "" { e.MetaName = e.calcBaseName(contentName) } return } | | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | if contentName := e.ContentName; contentName != "" { if !extIsMetaAndContent(e.ContentExt) && e.MetaName == "" { e.MetaName = e.calcBaseName(contentName) } return } syntax := m.GetDefault(api.KeySyntax, "") ext := calcContentExt(syntax, m.YamlSep, getZettelFileSyntax) metaName := e.MetaName eimc := extIsMetaAndContent(ext) if eimc { if metaName != "" { ext = contentExtWithMeta(syntax, content) } |
︙ | ︙ |
Changes to box/notify/fsdir.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "os" "path/filepath" |
︙ | ︙ | |||
54 55 56 57 58 59 60 | log.Error(). Str("parentDir", absParentDir).Err(errParent). Str("path", absPath).Err(err). Msg("Unable to access Zettel directory and its parent directory") watcher.Close() return nil, err } | > | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | log.Error(). Str("parentDir", absParentDir).Err(errParent). Str("path", absPath).Err(err). Msg("Unable to access Zettel directory and its parent directory") watcher.Close() return nil, err } log.Warn(). Str("parentDir", absParentDir).Err(errParent). Msg("Parent of Zettel directory cannot be supervised") log.Warn().Str("path", absPath). Msg("Zettelstore might not detect a deletion or movement of the Zettel directory") } else if err != nil { // Not a problem, if container is not available. It might become available later. log.Warn().Err(err).Str("path", absPath).Msg("Zettel directory not available") } fsdn := &fsdirNotifier{ log: log, events: make(chan Event), refresh: make(chan struct{}), done: make(chan struct{}), |
︙ | ︙ |
Changes to box/notify/helper.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "archive/zip" "os" "zettelstore.de/z/logger" ) // MakeMetaFilename builds the name of the file containing metadata. func MakeMetaFilename(basename string) string { return basename //+ ".meta" } // EntryFetcher return a list of (file) names of an directory. type EntryFetcher interface { Fetch() ([]string, error) } type dirPathFetcher struct { |
︙ | ︙ |
Changes to box/notify/notify.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package notify provides some notification services to be used by box services. package notify import "fmt" |
︙ | ︙ |
Changes to box/notify/simpledir.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package notify import ( "path/filepath" |
︙ | ︙ |
Changes to cmd/cmd_file.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package cmd import ( "context" "flag" "fmt" "io" "os" "zettelstore.de/client.fossil/api" "zettelstore.de/z/encoder" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // ---------- Subcommand: file ----------------------------------------------- func cmdFile(fs *flag.FlagSet) (int, error) { enc := fs.Lookup("t").Value.String() m, inp, err := getInput(fs.Args()) if m == nil { return 2, err } z := parser.ParseZettel( context.Background(), zettel.Zettel{ Meta: m, Content: zettel.NewContent(inp.Src[inp.Pos:]), }, m.GetDefault(api.KeySyntax, meta.SyntaxZmk), nil, ) encdr := encoder.Create(api.Encoder(enc), &encoder.CreateParameter{Lang: m.GetDefault(api.KeyLang, api.ValueLangEN)}) if encdr == nil { fmt.Fprintf(os.Stderr, "Unknown format %q\n", enc) return 2, nil } |
︙ | ︙ |
Changes to cmd/cmd_password.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package cmd import ( "flag" "fmt" |
︙ | ︙ |
Changes to cmd/cmd_run.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package cmd import ( "context" "flag" |
︙ | ︙ |
Changes to cmd/command.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package cmd import ( "flag" |
︙ | ︙ |
Changes to cmd/main.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package cmd import ( "crypto/sha256" "flag" "fmt" "net" "net/url" "os" "runtime/debug" "strconv" "strings" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" "zettelstore.de/z/auth/impl" "zettelstore.de/z/box" "zettelstore.de/z/box/compbox" "zettelstore.de/z/box/manager" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/kernel" "zettelstore.de/z/logger" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
︙ | ︙ | |||
233 234 235 236 237 238 239 | return err == nil } func setConfigValue(err error, subsys kernel.Service, key string, val any) error { if err == nil { err = kernel.Main.SetConfig(subsys, key, fmt.Sprint(val)) if err != nil { | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | return err == nil } func setConfigValue(err error, subsys kernel.Service, key string, val any) error { if err == nil { err = kernel.Main.SetConfig(subsys, key, fmt.Sprint(val)) if err != nil { kernel.Main.GetKernelLogger().Fatal().Str("key", key).Str("value", fmt.Sprint(val)).Err(err).Msg("Unable to set configuration") } } return err } func executeCommand(name string, args ...string) int { command, ok := Get(name) |
︙ | ︙ |
Changes to cmd/register.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package cmd provides command generic functions. package cmd // Mention all needed encoders, parsers and stores to have them registered. import ( |
︙ | ︙ |
Changes to cmd/zettelstore/main.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package main is the starting point for the zettelstore command. package main import ( "os" |
︙ | ︙ |
Changes to collect/collect.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" |
︙ | ︙ |
Changes to collect/collect_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package collect_test provides some unit test for collectors. package collect_test import ( "testing" |
︙ | ︙ |
Changes to collect/order.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" |
︙ | ︙ |
Added collect/split.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package collect provides functions to collect items from a syntax tree. package collect import ( "zettelstore.de/z/ast" "zettelstore.de/z/strfun" ) // DivideReferences divides the given list of rederences into zettel, local, and external References. func DivideReferences(all []*ast.Reference) (zettel, local, external []*ast.Reference) { if len(all) == 0 { return nil, nil, nil } mapZettel := make(strfun.Set) mapLocal := make(strfun.Set) mapExternal := make(strfun.Set) for _, ref := range all { if ref.State == ast.RefStateSelf { continue } if ref.IsZettel() { zettel = appendRefToList(zettel, mapZettel, ref) } else if ref.IsExternal() { external = appendRefToList(external, mapExternal, ref) } else { local = appendRefToList(local, mapLocal, ref) } } return zettel, local, external } func appendRefToList(reflist []*ast.Reference, refSet strfun.Set, ref *ast.Reference) []*ast.Reference { s := ref.String() if !refSet.Has(s) { reflist = append(reflist, ref) refSet.Set(s) } return reflist } |
Changes to config/config.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package config provides functions to retrieve runtime configuration data. package config import ( "context" |
︙ | ︙ |
Changes to docs/development/00010000000000.zettel.
1 2 3 4 5 | id: 00010000000000 title: Developments Notes role: zettel syntax: zmk created: 00010101000000 | | < | 1 2 3 4 5 6 7 8 9 10 | id: 00010000000000 title: Developments Notes role: zettel syntax: zmk created: 00010101000000 modified: 20221026184905 * [[Required Software|20210916193200]] * [[Fuzzing tests|20221026184300]] * [[Checklist for Release|20210916194900]] |
Changes to docs/development/20210916193200.zettel.
1 2 3 4 5 | id: 20210916193200 title: Required Software role: zettel syntax: zmk created: 20210916193200 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | id: 20210916193200 title: Required Software role: zettel syntax: zmk created: 20210916193200 modified: 20230405150541 The following software must be installed: * A current, supported [[release of Go|https://go.dev/doc/devel/release]], * [[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 ``` The internal build tool need the following software. It can be installed / updated via the build tool itself: ``go run tools/build.go tools``. Otherwise you can install the software by hand: * [[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``, |
Changes to docs/development/20210916194900.zettel.
1 2 3 4 5 | id: 20210916194900 title: Checklist for Release role: zettel syntax: zmk created: 20210916194900 | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | id: 20210916194900 title: Checklist for Release role: zettel syntax: zmk created: 20210916194900 modified: 20230402181229 # Sync with the official repository #* ``fossil sync -u`` # Make sure that there is no workspace defined. #* ``ls ..`` must not have a file ''go.work'', in no parent folder. # Make sure that all dependencies are up-to-date. #* ``cat go.mod`` # Clean up your Go workspace: #* ``go run tools/build.go clean`` (alternatively: ``make clean``). # All internal tests must succeed: #* ``go run tools/build.go relcheck`` (alternatively: ``make relcheck``). # The API tests must succeed on every development platform: #* ``go run tools/build.go testapi`` (alternatively: ``make api``). # Run [[linkchecker|https://linkchecker.github.io/linkchecker/]] with the manual: #* ``go run -race cmd/zettelstore/main.go run -d docs/manual`` #* ``linkchecker http://127.0.0.1:23123 2>&1 | tee lc.txt`` #* Check all ""Error: 404 Not Found"" #* Check all ""Error: 403 Forbidden"": allowed for endpoint ''/p'' with encoding ''html'' for those zettel that are accessible only in ''expert-mode''. #* Try to resolve other error messages and warnings #* Warnings about empty content can be ignored |
︙ | ︙ | |||
40 41 42 43 44 45 46 | # Disable Fossil autosync mode: #* ``fossil setting autosync off`` # Commit the new release version: #* ``fossil commit --tag release --tag vVERSION -m "Version VERSION"`` #* **Important:** the tag must follow the given pattern, e.g. ''v0.0.15''. Otherwise client will not be able to import ''zettelkasten.de/z''. # Clean up your Go workspace: | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | # Disable Fossil autosync mode: #* ``fossil setting autosync off`` # Commit the new release version: #* ``fossil commit --tag release --tag vVERSION -m "Version VERSION"`` #* **Important:** the tag must follow the given pattern, e.g. ''v0.0.15''. Otherwise client will not be able to import ''zettelkasten.de/z''. # Clean up your Go workspace: #* ``go run tools/build.go clean`` (alternatively: ``make clean``). # Create the release: #* ``go run tools/build.go release`` (alternatively: ``make release``). # Remove previous executables: #* ``fossil uv remove --glob '*-PREVVERSION*'`` # Add executables for release: #* ``cd releases`` #* ``fossil uv add *.zip`` #* ``cd ..`` #* Synchronize with main repository: #* ``fossil sync -u`` # Enable autosync: #* ``fossil setting autosync on`` |
Deleted docs/development/20231218181900.zettel.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to docs/manual/00001004010000.zettel.
1 2 3 4 5 6 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 modified: 20221128155143 The configuration file, as specified by the ''-c CONFIGFILE'' [[command line option|00001004051000]], allows you to specify some startup options. These options cannot be stored in a [[configuration zettel|00001004020000]] because either they are needed before Zettelstore can start or because of security reasons. For example, Zettelstore need to know in advance, on which network address is must listen or where zettel are stored. An attacker that is able to change the owner can do anything. Therefore only the owner of the computer on which Zettelstore runs can change this information. |
︙ | ︙ | |||
88 89 90 91 92 93 94 | Can be changed at runtime, even for specific internal services, with the ''log-level'' command of the [[administrator console|00001004101000#log-level]]. Several specifications are separated by the semicolon character (""'';''"", U+003B). Each specification consists of an optional service name, together with the colon character (""'':''"", U+003A), followed by the logging level. Default: ""info"". | | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | Can be changed at runtime, even for specific internal services, with the ''log-level'' command of the [[administrator console|00001004101000#log-level]]. Several specifications are separated by the semicolon character (""'';''"", U+003B). Each specification consists of an optional service name, together with the colon character (""'':''"", U+003A), followed by the logging level. Default: ""info"". Examples: ""sense"" will produce sensing messages (e.g. a little more than ""info""); ""sense;web:debug"" will emit debugging messages for the web component of Zettelstore while still producing sensing messages for all other components. When you are familiar to operate the Zettelstore, you might set the level to ""warn"" or ""error"" to receive less noisy messages from the Zettelstore. ; [!max-request-size|''max-request-size''] : Limits the maximum byte size of a web request body to prevent clients from accidentally or maliciously sending a large request and wasting server resources. The minimum value is 1024. Default: 16777216 (16 MiB). ; [!owner|''owner''] : [[Identifier|00001006050000]] of a zettel that contains data about the owner of the Zettelstore. |
︙ | ︙ |
Changes to docs/manual/00001004059700.zettel.
1 2 3 4 5 | id: 00001004059700 title: List of supported logging levels role: manual tags: #configuration #manual #zettelstore syntax: zmk | < | | > | > | > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | id: 00001004059700 title: List of supported logging levels role: manual tags: #configuration #manual #zettelstore syntax: zmk modified: 20220113183606 Zettelstore supports various levels of logging output. This allows you to see the inner workings of Zettelstore, or to avoid it. Each level has an associated name and number. A lower number signals more logging output. |= Name | Number >| Description | Trace | 1 | Show most of the inner workings | Debug | 2 | Show many internal values that might be interesting for a [[Zettelstore developer|00000000000005]]. | Sense | 3 | Display sensing events, which are not essential information. | Info | 4 | Display information about an event. In most cases, there is no required action expected from you. | Warn | 5 | Show a warning, i.e. an event that might become an error or more. Mostly invalid data. | Error | 6 | Notify about an error, which was handled automatically. Something is broken. User intervention is not required, in most cases. Monitor the application. | Fatal | 7 | Notify about a significant error that cannot be handled automatically. At least some important functionality is disabled. | Panic | 8 | The application is in an uncertain state and notifies you about its panic. At least some part of the application is possibly restarted. | Mandatory | 9 | Important message will be shown, e.g. the Zettelstore version at startup time. | Disabled | 10 | No messages will be shown If you set the logging level to a certain value, only messages with the same or higher numerical value will be shown. E.g. if you set the logging level to ""warn"", no ""trace"", ""debug"", ""sense", and ""info"" messages are shown, but ""warn"", ""error"", ""fatal"", ""panic"", and ""mandatory"" messages. |
Changes to docs/manual/00001006010000.zettel.
1 2 3 4 5 | id: 00001006010000 title: Syntax of Metadata role: manual tags: #manual #syntax #zettelstore syntax: zmk | < | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | id: 00001006010000 title: Syntax of Metadata role: manual tags: #manual #syntax #zettelstore syntax: zmk modified: 20220218131923 The metadata of a zettel is a collection of key-value pairs. The syntax roughly resembles the internal header of an email ([[RFC5322|https://tools.ietf.org/html/rfc5322]]). The key is a sequence of alphanumeric characters, a hyphen-minus character (""''-''"", U+002D) is also allowed. It begins at the first position of a new line. A key is separated from its value either by * a colon character (""'':''""), * a non-empty sequence of space characters, * a sequence of space characters, followed by a colon, followed by a sequence of space characters. A Value is a sequence of printable characters. If the value should be continued in the following line, that following line (""continuation line"") must begin with a non-empty sequence of space characters. The rest of the following line will be interpreted as the next part of the value. There can be more than one continuation line for a value. A non-continuation line that contains a possibly empty sequence of characters, followed by the percent sign character (""''%''"") is treated as a comment line. It will be ignored. |
︙ | ︙ |
Changes to docs/manual/00001006030000.zettel.
1 2 3 4 5 6 | id: 00001006030000 title: Supported Key Types role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210126175322 | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | id: 00001006030000 title: Supported Key Types role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210126175322 modified: 20230612183742 All [[supported metadata keys|00001006020000]] conform to a type. User-defined metadata keys conform also to a type, based on the suffix of the key. |=Suffix|Type | ''-date'' | [[Timestamp|00001006034500]] | ''-number'' | [[Number|00001006033000]] | ''-role'' | [[Word|00001006035500]] | ''-set'' | [[WordSet|00001006036000]] | ''-time'' | [[Timestamp|00001006034500]] | ''-title'' | [[Zettelmarkup|00001006036500]] | ''-url'' | [[URL|00001006035000]] | ''-zettel'' | [[Identifier|00001006032000]] | ''-zid'' | [[Identifier|00001006032000]] | ''-zids'' | [[IdentifierSet|00001006032500]] | any other suffix | [[EString|00001006031500]] |
︙ | ︙ | |||
34 35 36 37 38 39 40 41 | * [[IdentifierSet|00001006032500]] * [[Number|00001006033000]] * [[String|00001006033500]] * [[TagSet|00001006034000]] * [[Timestamp|00001006034500]] * [[URL|00001006035000]] * [[Word|00001006035500]] * [[Zettelmarkup|00001006036500]] | > | 35 36 37 38 39 40 41 42 43 | * [[IdentifierSet|00001006032500]] * [[Number|00001006033000]] * [[String|00001006033500]] * [[TagSet|00001006034000]] * [[Timestamp|00001006034500]] * [[URL|00001006035000]] * [[Word|00001006035500]] * [[WordSet|00001006036000]] * [[Zettelmarkup|00001006036500]] |
Added docs/manual/00001006036000.zettel.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | id: 00001006036000 title: WordSet Key Type role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210212135017 modified: 20230419175745 Values of this type denote a (sorted) set of [[words|00001006035500]]. A set is different to a list, as no duplicate values are allowed. === Allowed values Must be a sequence of at least one word, separated by space characters. === Query comparison All comparisons are done case-insensitive, i.e. ""hell"" will be the prefix of ""World, Hello"". === Sorting Sorting is done by comparing the [[String|00001006033500]] values. |
Changes to docs/manual/00001007031140.zettel.
1 2 3 4 5 6 | id: 00001007031140 title: Zettelmarkup: Query Transclusion role: manual tags: #manual #search #zettelmarkup #zettelstore syntax: zmk created: 20220809132350 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001007031140 title: Zettelmarkup: Query Transclusion role: manual tags: #manual #search #zettelmarkup #zettelstore syntax: zmk created: 20220809132350 modified: 20231023163751 A query transclusion is specified by the following sequence, starting at the first position in a line: ''{{{query:query-expression}}}''. The line must literally start with the sequence ''{{{query:''. Everything after this prefix is interpreted as a [[query expression|00001007700000]]. When evaluated, the query expression is evaluated, often resulting in a list of [[links|00001007040310]] to zettel, matching the query expression. The result replaces the query transclusion element. |
︙ | ︙ | |||
45 46 47 48 49 50 51 | : Transform the zettel list into an [[Atom 1.0|https://www.rfc-editor.org/rfc/rfc4287]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''RSS'' (aggregate) : Transform the zettel list into a [[RSS 2.0|https://www.rssboard.org/rss-specification]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''KEYS'' (aggregate) : Emit a list of all metadata keys, together with the number of zettel having the key. | | | | < < < | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | : Transform the zettel list into an [[Atom 1.0|https://www.rfc-editor.org/rfc/rfc4287]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''RSS'' (aggregate) : Transform the zettel list into a [[RSS 2.0|https://www.rssboard.org/rss-specification]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''KEYS'' (aggregate) : Emit a list of all metadata keys, together with the number of zettel having the key. ; ''REINDEX'' (aggregate) : Will be ignored. This action may have been copied from an existing [[API query call|00001012051400]] (or from a WebUI query), but is here superfluous (and possibly harmful). ; Any [[metadata key|00001006020000]] of type [[Word|00001006035500]], [[WordSet|00001006036000]], or [[TagSet|00001006034000]] (aggregates) : Emit an aggregate of the given metadata key. The key can be given in any letter case[^Except if the key name collides with one of the above names. In this case use at least one lower case letter.]. Example: ```zmk {{{query:tags:#search | tags}}} ``` This is a tag cloud of all tags that are used together with the tag #search: :::example {{{query:tags:#search | tags}}} ::: |
Changes to docs/manual/00001007040324.zettel.
1 2 3 4 5 6 | id: 00001007040324 title: Zettelmarkup: Inline-mode Transclusion role: manual tags: #manual #zettelmarkup #zettelstore syntax: zmk created: 20210811154251 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001007040324 title: Zettelmarkup: Inline-mode Transclusion role: manual tags: #manual #zettelmarkup #zettelstore syntax: zmk created: 20210811154251 modified: 20221116165428 Inline-mode transclusion applies to all zettel that are parsed in a non-trivial way, e.g. as structured textual content. For example, textual content is assumed if the [[syntax|00001006020000#syntax]] of a zettel is ""zmk"" ([[Zettelmarkup|00001007000000]]), or ""markdown"" / ""md"" ([[Markdown|00001008010000]]). Since this type of transclusion is at the level of [[inline-structured elements|00001007040000]], the transclude specification must be replaced with some inline-structured elements. First, the referenced zettel is read. |
︙ | ︙ | |||
34 35 36 37 38 39 40 | Example: ``{{00001007040322#spin}}`` is rendered as ::{{00001007040322#spin}}::{=example}. ** Just specifying the fragment identifier will reference something in the current page. This is not allowed, to prevent a possible endless recursion. * If the reference is a [[hosted or based|00001007040310#link-specifications]] link / URL to an image, that image will be rendered. | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | Example: ``{{00001007040322#spin}}`` is rendered as ::{{00001007040322#spin}}::{=example}. ** Just specifying the fragment identifier will reference something in the current page. This is not allowed, to prevent a possible endless recursion. * If the reference is a [[hosted or based|00001007040310#link-specifications]] link / URL to an image, that image will be rendered. Example: ``{{//z/00000000040001}}`` is rendered as ::{{//z/00000000040001}}::{=example} If no inline-structured elements are found, the transclude specification is replaced by an error message. To avoid an exploding ""transclusion bomb"", a form of a [[billion laughs attack|https://en.wikipedia.org/wiki/Billion_laughs_attack]] (also known as ""XML bomb""), the total number of transclusions / expansions is limited. The limit can be controlled by setting the value [[''max-transclusions''|00001004020000#max-transclusions]] of the runtime configuration zettel. === See also [[Full transclusion|00001007031100]] does not work inside some text, but is used for [[block-structured elements|00001007030000]]. |
Changes to docs/manual/00001007720300.zettel.
1 2 3 4 5 6 | id: 00001007720300 title: Query: Context Directive role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707204706 | | < | | < | | | | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | id: 00001007720300 title: Query: Context Directive role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707204706 modified: 20230724153832 A context directive calculates the __context__ of a list of zettel identifier. It starts with the keyword ''CONTEXT''. Optionally you may specify some context details, after the keyword ''CONTEXT'', separated by space characters. These are: * ''BACKWARD'': search for context only though backward links, * ''FORWARD'': search for context only through forward links, * ''COST'', one or more space characters, and a positive integer: set the maximum __cost__ (default: 17), * ''MAX'', one or more space characters, and a positive integer: set the maximum number of context zettel (default: 200). If no ''BACKWARD'' and ''FORWARD'' is specified, a search for context zettel will be done though backward and forward links. The cost of a context zettel is calculated iteratively: * Each of the specified zettel hast a cost of one. * A zettel found as a single folge zettel or single precursor zettel has the cost of the originating zettel, plus one. * A zettel found as a single successor zettel or single predecessor zettel has the cost of the originating zettel, plus two. * A zettel found via another link without being part of a [[set of zettel identifier|00001006032500]], has the cost of the originating zettel, plus three. * A zettel which is part of a set of zettel identifier, has the cost of the originating zettel, plus one of the three choices above and multiplied with roughly a logarithmic value based on the size of the set. * A zettel with the same tag, has the cost of the originating zettel, plus the number of zettel with the same tag (if it is less than eight), or the cost of the originating zettel plus two, multiplied by number of zettel with the same tag divided by four. The maximum cost is only checked for all zettel that are not directly reachable from the initial, specified list of zettel. This ensures that initial zettel that have only a highly used tag, will also produce some context zettel. Despite its possibly complicated structure, this algorithm ensures in practice that the zettel context is a list of zettel, where the first elements are ""near"" to the specified zettel and the last elements are more ""distant"" to the specified zettel. It also penalties zettel that acts as a ""hub"" to other zettel, to make it more likely that only relevant zettel appear on the context list. This directive may be specified only once as a query directive. A second occurence of ''CONTEXT'' is interpreted as a [[search expression|00001007701000]]. In most cases it is easier to adjust the maximum cost than to perform another context search, which is relatively expensive in terms of retrieving power. |
Changes to docs/manual/00001007770000.zettel.
1 2 3 4 5 6 | id: 00001007770000 title: Query: Action List role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707205246 | | | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | id: 00001007770000 title: Query: Action List role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707205246 modified: 20230707205532 With a [[list of zettel identifier|00001007710000]], a [[query directives|00001007720000]], or a [[search expression|00001007701000]], a list of zettel is selected. __Actions__ allow to modify this list to a certain degree. Which actions are allowed depends on the context. However, actions are further separated into __parameter action__ and __aggregate actions__. A parameter action just sets a parameter for an aggregate action. An aggregate action transforms the list of selected zettel into a different, aggregate form. Only the first aggregate form is executed, following aggregate actions are ignored. In most contexts, valid actions include the name of metadata keys, at least of type [[Word|00001006035500]], [[WordSet|00001006036000]], or [[TagSet|00001006034000]]. |
Changes to docs/manual/00001007780000.zettel.
1 2 3 4 5 6 | id: 00001007780000 title: Formal syntax of query expressions role: manual tags: #manual #reference #search #zettelstore syntax: zmk created: 20220810144539 | | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | id: 00001007780000 title: Formal syntax of query expressions role: manual tags: #manual #reference #search #zettelstore syntax: zmk created: 20220810144539 modified: 20230731160413 ``` QueryExpression := ZettelList? QueryDirective* SearchExpression ActionExpression? ZettelList := (ZID (SPACE+ ZID)*). ZID := '0'+ ('1' .. '9'') DIGIT* | ('1' .. '9') DIGIT*. QueryDirective := ContextDirective | IdentDirective | ItemsDirective | UnlinkedDirective. ContextDirective := "CONTEXT" (SPACE+ ContextDetail)*. ContextDetail := "BACKWARD" | "FORWARD" | "COST" SPACE+ PosInt | "MAX" SPACE+ PosInt. IdentDirective := IDENT. ItemsDirective := ITEMS. UnlinkedDirective := UNLINKED (SPACE+ PHRASE SPACE+ Word)*. SearchExpression := SearchTerm (SPACE+ SearchTerm)*. |
︙ | ︙ | |||
39 40 41 42 43 44 45 | SearchOperator := '!' | ('!')? ('~' | ':' | '[' | '}'). ExistOperator := '?' | '!' '?'. PosInt := '0' | ('1' .. '9') DIGIT*. ActionExpression := '|' (Word (SPACE+ Word)*)? | < < < < < < < < < < | 38 39 40 41 42 43 44 45 46 | SearchOperator := '!' | ('!')? ('~' | ':' | '[' | '}'). ExistOperator := '?' | '!' '?'. PosInt := '0' | ('1' .. '9') DIGIT*. ActionExpression := '|' (Word (SPACE+ Word)*)? Word := NO-SPACE NO-SPACE* ``` |
Changes to docs/manual/00001007790000.zettel.
1 2 3 4 5 6 | id: 00001007790000 title: Useful query expressions role: manual tags: #example #manual #search #zettelstore syntax: zmk created: 20220810144539 | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | id: 00001007790000 title: Useful query expressions role: manual tags: #example #manual #search #zettelstore syntax: zmk created: 20220810144539 modified: 20230706155134 |= 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, ordered by zettel identifier | [[query:dead?]] | Zettel with invalid / dead links | [[query:backward!? precursor!?]] | Zettel that are not referenced by other zettel | [[query:tags!?]] | Zettel without tags | [[query:expire? ORDER expire]] | Zettel with an expire date, ordered from the nearest to the latest | [[query:00001007700000 CONTEXT]] | Zettel within the context of the [[given zettel|00001007700000]] |
Changes to docs/manual/00001007903000.zettel.
1 2 3 4 5 6 | id: 00001007903000 title: Zettelmarkup: First Steps role: manual tags: #manual #tutorial #zettelmarkup #zettelstore syntax: zmk created: 20220810182917 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001007903000 title: Zettelmarkup: First Steps role: manual tags: #manual #tutorial #zettelmarkup #zettelstore syntax: zmk created: 20220810182917 modified: 20220926183359 [[Zettelmarkup|00001007000000]] allows you to leave your text as it is, at least in many situations. Some characters have a special meaning, but you have to enter them is a defined way to see a visible change. Zettelmarkup is designed to be used for zettel, which are relatively short. It allows to produce longer texts, but you should probably use a different tool, if you want to produce an scientific paper, to name an example. === Paragraphs |
︙ | ︙ | |||
27 28 29 30 31 32 33 | | ''An __emphasized__ word'' | An __emphasized__ word | Put two underscore characters before and after the text you want to emphasize | ''Someone uses **bold** text'' | Someone uses **bold** text | Put two asterisks before and after the text you want to see bold | ''He says: ""I love you!""'' | Her says: ""I love you!"" | Put two quotation mark characters before and after the text you want to quote. You probably see a principle. One nice thing about the quotation mark characters: they are rendered according to the current language. | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | | ''An __emphasized__ word'' | An __emphasized__ word | Put two underscore characters before and after the text you want to emphasize | ''Someone uses **bold** text'' | Someone uses **bold** text | Put two asterisks before and after the text you want to see bold | ''He says: ""I love you!""'' | Her says: ""I love you!"" | Put two quotation mark characters before and after the text you want to quote. You probably see a principle. One nice thing about the quotation mark characters: they are rendered according to the current language. Examples: ""english""{lang=en}, ""french""{lang=fr}, ""german""{lang=de}, ""finnish""{lang=fi}. You will see later, how to change the current language. === Lists Quite often, text consists of lists. Zettelmarkup supports different types of lists. The most important lists are: * Unnumbered lists, |
︙ | ︙ |
Changes to docs/manual/00001007990000.zettel.
1 2 3 4 5 6 | id: 00001007990000 title: Zettelmarkup: Cheat Sheet role: manual tags: #manual #reference #zettelmarkup syntax: zmk created: 20221209191905 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | id: 00001007990000 title: Zettelmarkup: Cheat Sheet role: manual tags: #manual #reference #zettelmarkup syntax: zmk created: 20221209191905 modified: 20221209193310 === Overview This Zettelmarkup cheat sheet provides a quick overview of many Zettelmarkup elements. It can not cover any special case. If you need more information about any of these elements, please refer to the detailed description. === Basic Syntax |[[Text formatting|00001007040100]]|''__italic text__'' → __italic text__, ''**bold text**'' → **bold text**, ''""quoted text""'' → ""quoted text"" |[[Text editing|00001007040100]]|''>>inserted text>>'' → >>inserted text>>, ''~~deleted text~~'' → ~~deleted text~~ |[[Text literal formatting|00001007040200]]|''\'\'entered text\'\''' → ''entered text'', ''``source code``'' → ``source code``, ''==text output=='' → ==text output== |[[Superscript, subscript|00001007040100]]|''m^^2^^'' → m^^2^^, ''H,,2,,O'' → H,,2,,O |[[Links to other zettel|00001007040310]]|''[[Link text|00001007990000]]'' → [[Link text|00001007990000]] |[[Links to external resources|00001007040310]]|''[[Zettelstore|https://zettelstore.de]]'' → [[Zettelstore|https://zettelstore.de]] |[[Embed an image|00001007040322]]|''{{Image text|00000000040001}}'' → {{Image text|00000000040001}} |[[Embed content of first paragraph|00001007040324]]|''{{00001007990000}}'' → {{00001007990000}} |
︙ | ︙ |
Changes to docs/manual/00001012051400.zettel.
1 2 3 4 5 6 | id: 00001012051400 title: API: Query the list of all zettel role: manual tags: #api #manual #zettelstore syntax: zmk created: 20220912111111 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012051400 title: API: Query the list of all zettel role: manual tags: #api #manual #zettelstore syntax: zmk created: 20220912111111 modified: 20231023162927 precursor: 00001012051200 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. An empty list of action, or no valid action, returns the list of all selected zettel metadata. |
︙ | ︙ | |||
105 106 107 108 109 110 111 | The following actions are supported: ; ''MINn'' (parameter) : Emit only those values with at least __n__ aggregated values. __n__ must be a positive integer, ''MIN'' must be given in upper-case letters. ; ''MAXn'' (parameter) : Emit only those values with at most __n__ aggregated values. __n__ must be a positive integer, ''MAX'' must be given in upper-case letters. | < < < | < | < < < < | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | The following actions are supported: ; ''MINn'' (parameter) : Emit only those values with at least __n__ aggregated values. __n__ must be a positive integer, ''MIN'' must be given in upper-case letters. ; ''MAXn'' (parameter) : Emit only those values with at most __n__ aggregated values. __n__ must be a positive integer, ''MAX'' must be given in upper-case letters. ; ''REINDEX'' (aggregate) : Updates the internal search index for the selected zettel, roughly similar to the [[refresh|00001012080500]] API call. It is not really an aggregate, since it is used only for its side effect. It is allowed to specify another aggregate. ; Any [[metadata key|00001006020000]] of type [[Word|00001006035500]], [[WordSet|00001006036000]], or [[TagSet|00001006034000]] (aggregates) : Emit an aggregate of the given metadata key. The key can be given in any letter case. Only the first aggregate action will be executed. === HTTP Status codes ; ''200'' : Query was successful. ; ''204'' : Query was successful, but results in no content. Most likely, you specified no appropriate aggregator. ; ''400'' : Request was not valid. There are several reasons for this. Maybe the access bearer token was not valid, or you forgot to specify a valid query. |
Changes to docs/manual/00001012931000.zettel.
1 2 3 4 5 6 | id: 00001012931000 title: Encoding of Sz role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403153903 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931000 title: Encoding of Sz role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403153903 modified: 20230405133249 Zettel in a [[Sz encoding|00001012920516]] are represented as a [[symbolic expression|00001012930000]]. To process these symbolic expressions, you need to know, how a specific part of a zettel is represented by a symbolic expression. Basically, each part of a zettel is represented as a list, often a nested list. The first element of that list is always an unique symbol, which denotes that part. The meaning / semantic of all other elements depend on that symbol. |
︙ | ︙ | |||
25 26 27 28 29 30 31 | Metadata is represented by a list, where the first element is the symbol ''META''. Following elements represent each metadatum[^""Metadatum"" is used as the singular form of metadata.] of a zettel in standard order. Standard order is: [[Title|00001006020000#title]], [[Role|00001006020000#role]], [[Tags|00001006020000#tags]], [[Syntax|00001006020000#syntax]], all other [[keys|00001006020000]] in alphabetic order. :::syntax | | | | > | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | Metadata is represented by a list, where the first element is the symbol ''META''. Following elements represent each metadatum[^""Metadatum"" is used as the singular form of metadata.] of a zettel in standard order. Standard order is: [[Title|00001006020000#title]], [[Role|00001006020000#role]], [[Tags|00001006020000#tags]], [[Syntax|00001006020000#syntax]], all other [[keys|00001006020000]] in alphabetic order. :::syntax __Metadata__ **=** ''(META'' [[__Metadatum__|00001012931200]] __Metadatum__ … __Metadatum__ '')''. ::: === Content Zettel content is represented by a block. :::syntax __Content__ **=** [[__Block__|#block]]. ::: ==== Block A block is represented by a list with the symbol ''BLOCK'' as the first element. All following elements represent a nested [[block-structured element|00001007030000]]. :::syntax [!block|__Block__] **=** ''(BLOCK'' [[__BlockElement__|00001012931400]] __BlockElement__ … __BlockElement__ '')''. ::: ==== Inline Both block-structured elements and some metadata values may contain [[inline-structured elements|00001007040000]]. Similar, inline-structured elements are represented as follows: :::syntax __Inline__ **=** ''(INLINE'' [[__InlineElement__|00001012931600]] __InlineElement__ … __InlineElement__ '')''. ::: ==== Attribute [[Attributes|00001007050000]] may be specified for both block- and inline- structured elements. Attributes are represented by the following schema. Please note, the the symbol ''quote'' is lower-case by intention. :::syntax __Attribute__ **=** ''('' **[** ''quote'' ''('' [[__AttributeKeyValue__|00001012931800]] __AttributeKeyValue__ … __AttributeKeyValue__ '')'' **]** ')'. ::: Either, there are no attributes. These are specified by the empty list ''()''. Or there are attributes. In this case, the first element of the list must be the symbol ''quote'': ''(quote'' ''('' A,,1,, A,,2,, … A,,n,, '')'''')''. |
︙ | ︙ |
Changes to docs/manual/00001012931200.zettel.
1 2 3 4 5 6 | id: 00001012931200 title: Encoding of Sz Metadata role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161618 | | > > > | | > > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | id: 00001012931200 title: Encoding of Sz Metadata role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161618 modified: 20230405121932 A single metadata (""metadatum"") is represented by a triple: a symbol representing the type, a symbol representing the key, and either a string or a list that represent the value. The key symbol must be ""quoted"", i.e. for the key ""title"": ''(quote title)''. This property may be relaxed in future versions of the Zettelstore. The symbol depends on the [[metadata key type|00001006030000]]. The value also depends somehow on the key type: a set of values is represented as a list, all other values are represented by a string, even if it is a number. The following table maps key types to symbols and to the type of the value representation. |=Key Type<| Symbol<| Value< | [[Credential|00001006031000]] | ''CREDENTIAL'' | string | [[EString|00001006031500]] | ''EMPTY-STRING'' | string | [[Identifier|00001006032000]] | ''ZID'' | string | [[IdentifierSet|00001006032500]] | ''ZID-SET'' | list | [[Number|00001006033000]] | ''NUMBER'' | string | [[String|00001006033500]] | ''STRING'' | string | [[TagSet|00001006034000]] | ''TAG-SET'' | list | [[Timestamp|00001006034500]] | ''TIMESTAMP'' | string | [[URL|00001006035000]] | ''URL'' | string | [[Word|00001006035500]] | ''WORD'' | string | [[WordSet|00001006036000]] | ''WORD-SET'' | list | [[Zettelmarkup|00001006036500]] | ''ZETTELMARKUP'' | string If the value is represented as a list, its first element is the symbol ''list'', and all other elements are strings with the appropriate values. :::syntax __ListValue__ **=** ''(list'' String,,1,, String,,2,, … String,,n,, '')''. ::: Examples: * The title of this zettel is represented as: ''(EMPTY-STRING (quote title) "Encoding of Sz Metadata")'' * The tags of this zettel are represented as: ''(TAG-SET (quote tags) (list "#api" "#manual" "#reference" "#zettelstore"))'' |
Changes to docs/manual/00001012931400.zettel.
1 2 3 4 5 6 | id: 00001012931400 title: Encoding of Sz Block Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161803 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931400 title: Encoding of Sz Block Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161803 modified: 20230405132916 === ''PARA'' :::syntax __Paragraph__ **=** ''(PARA'' [[__InlineElement__|00001012931600]] … '')''. ::: A paragraph is just a list of inline elements. |
︙ | ︙ | |||
46 47 48 49 50 51 52 | === ''DESCRIPTION'' :::syntax __Description__ **=** ''(DESCRIPTION'' __DescriptionTerm__ __DescriptionValues__ __DescriptionTerm__ __DescriptionValues__ … '')''. ::: A description is a sequence of one ore more terms and values. :::syntax | | | | | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | === ''DESCRIPTION'' :::syntax __Description__ **=** ''(DESCRIPTION'' __DescriptionTerm__ __DescriptionValues__ __DescriptionTerm__ __DescriptionValues__ … '')''. ::: A description is a sequence of one ore more terms and values. :::syntax __DescriptionTerm__ **=** [[__Inline__|00001012931000#inline]]. ::: A description term is just an inline-structured value. :::syntax __DescriptionValues__ **=** ''(BLOCK'' [[__Block__|00001012931000#block]] … '')''. ::: Description values are sequences of blocks. === ''TABLE'' :::syntax __Table__ **=** ''(TABLE'' __TableHeader__ __TableRow__ … '')''. ::: A table is a table header and a sequence of table rows. :::syntax __TableHeader__ **=** ''()'' **|** ''(list'' __TableCell__ … '')''. ::: A table header is either the empty list or a list of table cells stating with the ''list'' symbol. :::syntax __TableRow__ **=** ''(list'' __TableCell__ … '')''. ::: A table row is a list with the initial symbol ''list'', followed by table cells. === ''CELL'', ''CELL-*'' There are four kinds of table cells, one for each possible cell alignment. The structure is the same for all kind. :::syntax __TableCell__ **=** __DefaultCell__ **|** __CenterCell__ **|** __LeftCell__ **|** __RightCell__. |
︙ | ︙ | |||
105 106 107 108 109 110 111 | === ''REGION-*'' The following lists specifies different kinds of regions. A region treat a sequence of block elements to be belonging together in certain ways. The have a similar structure. :::syntax | | | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | === ''REGION-*'' The following lists specifies different kinds of regions. A region treat a sequence of block elements to be belonging together in certain ways. The have a similar structure. :::syntax __BlockRegion__ **=** ''(REGION-BLOCK'' [[__Attributes__|00001012931000#attribute]] [[__Block__|00001012931000#block]] [[__Inline__|00001012931000#inline]] '')''. ::: A block region just treats the block to belong in an unspecified way. Typically, the reason is given in the attributes. The inline describes the block. :::syntax __QuoteRegion__ **=** ''(REGION-QUOTE'' [[__Attributes__|00001012931000#attribute]] [[__Block__|00001012931000#block]] [[__Inline__|00001012931000#inline]] '')''. ::: A block region just treats the block to contain a longer quotation. Attributes may further specify the quotation. The inline typically describes author / source of the quotation. :::syntax __VerseRegion__ **=** ''(REGION-VERSE'' [[__Attributes__|00001012931000#attribute]] [[__Block__|00001012931000#block]] [[__Inline__|00001012931000#inline]] '')''. ::: A block region just treats the block to contain a verse. Soft line break are transformed into hard line breaks to save the structure of the verse / poem. Attributes may further specify something. The inline typically describes author / source of the verse. === ''VERBATIM-*'' |
︙ | ︙ | |||
163 164 165 166 167 168 169 | :::syntax __ZettelVerbatim__ **=** ''(VERBATIM-ZETTEL'' [[__Attributes__|00001012931000#attribute]] String '')''. ::: The string contains text that should be treated as (nested) zettel content. === ''BLOB'' :::syntax | | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | :::syntax __ZettelVerbatim__ **=** ''(VERBATIM-ZETTEL'' [[__Attributes__|00001012931000#attribute]] String '')''. ::: The string contains text that should be treated as (nested) zettel content. === ''BLOB'' :::syntax __BLOB__ **=** ''(BLOB'' [[__Inline__|00001012931000#inline]] String,,1,, String,,2,, '')''. ::: A BLOB contains an image in block mode. The inline states some description. The first string contains the syntax of the image. The second string contains the actual image. If the syntax is ""SVG"", then the second string contains the SVG code. Otherwise the (binary) image data is encoded with base64. === ''TRANSCLUDE'' :::syntax __Transclude__ **=** ''(TRANSCLUDE'' [[__Attributes__|00001012931000#attribute]] [[__Reference__|00001012931900]] '')''. ::: A transclude list only occurs for a parsed zettel, but not for a evaluated zettel. Evaluating a zettel also means that all transclusions are resolved. __Reference__ denotes the zettel to be transcluded. |
Changes to docs/manual/00001012931600.zettel.
1 2 3 4 5 6 | id: 00001012931600 title: Encoding of Sz Inline Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161845 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931600 title: Encoding of Sz Inline Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161845 modified: 20231113191203 === ''TEXT'' :::syntax __Text__ **=** ''(TEXT'' String '')''. ::: Specifies the string as some text content, typically a word. |
︙ | ︙ | |||
120 121 122 123 124 125 126 | The first string is the mark string used in the content. The second string is the mark string transformed to a slug, i.e. transformed to lower case, remove non-ASCII characters. The third string is the slug string, but made unique for the whole zettel. Then follows the marked text as a sequence of __InlineElement__s. === ''ENDNOTE'' :::syntax | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | The first string is the mark string used in the content. The second string is the mark string transformed to a slug, i.e. transformed to lower case, remove non-ASCII characters. The third string is the slug string, but made unique for the whole zettel. Then follows the marked text as a sequence of __InlineElement__s. === ''ENDNOTE'' :::syntax __Endnote__ **=** ''(ENDNOTE'' [[__Attributes__|00001012931000#attribute]] ''(quote'' [[__InlineElement__|00001012931600]] … '')'''')''. ::: Specifies endnote / footnote text. === ''FORMAT-*'' The following lists specifies some inline text formatting. The structure is always the same, the initial symbol denotes the actual formatting. |
︙ | ︙ |
Changes to docs/manual/00001012931800.zettel.
1 2 3 4 5 6 | id: 00001012931800 title: Encoding of Sz Attribute Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161923 | | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | id: 00001012931800 title: Encoding of Sz Attribute Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161923 modified: 20230403163701 An attribute is represented by a single cell. The first element of the cell references the attribute key, the second value the corresponding value. :::syntax __AttributeKeyValue__ **=** ''('' __AttributeKey__ ''.'' __AttributeValue__ '')''. ::: __AttributeKey__ and __AttributeValue__ are [[string values|00001012930500]]. An empty key denotes the generic attribute. A key with the value ''"-"'' specifies the default attribute. In this case, the attribute value is not interpreted. Some examples: * ''()'' represents the absence of attributes, * ''(quote (("-" . "")))'' represent the default attribute, * ''(quote (("-" . "") ("" . "syntax")))'' adds the generic attrribute with the value ""syntax"", * ''(quote ())'' will also represent the absence of attribute (in a more complicated way), * ''(quote (("lang" . "en")))'' denote the attribute key ""lang"" with a value ""en"". |
Changes to docs/manual/00001012931900.zettel.
1 2 3 4 5 6 | id: 00001012931900 title: Encoding of Sz Reference Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230405123046 | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | id: 00001012931900 title: Encoding of Sz Reference Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230405123046 modified: 20230405124516 A reference is encoded as the actual reference value, and a symbol describing the state of that actual reference value. :::syntax __Reference__ **=** ''(quote'' __ReferenceState__ String '')''. ::: The ''quote'' is needed for internal reasons, the string contains the actual reference value. :::syntax __ReferenceState__ **=** ''INVALID'' **|** ''ZETTEL'' **|** ''SELF'' **|** ''FOUND'' **|** ''BROKEN'' **|** ''HOSTED'' **|** ''BASED'' **|** ''QUERY'' **|** ''EXTERNAL''. ::: The meaning of the state symbols corresponds to that of the symbols used for the description of [[link references|00001012931600#link]]. |
︙ | ︙ | |||
29 30 31 32 33 34 35 | ; ''FOUND'' : The reference value is a valid reference to an existing zettel. This value is only possible after evaluating the zettel. ; ''BROKEN'' : The reference value is a valid reference to an missing zettel. This value is only possible after evaluating the zettel. ; ''HOSTED'' | < < < < | 29 30 31 32 33 34 35 36 37 38 | ; ''FOUND'' : The reference value is a valid reference to an existing zettel. This value is only possible after evaluating the zettel. ; ''BROKEN'' : The reference value is a valid reference to an missing zettel. This value is only possible after evaluating the zettel. ; ''HOSTED'' ; ''BASED'' ; ''QUERY'' ; ''EXTERNAL'' |
Changes to docs/manual/00001018000000.zettel.
1 2 3 4 5 | id: 00001018000000 title: Troubleshooting role: manual tags: #manual #zettelstore syntax: zmk | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001018000000 title: Troubleshooting role: manual tags: #manual #zettelstore syntax: zmk created: 00010101000000 modified: 20221020132617 This page lists some problems and their solutions that may occur when using your Zettelstore. === Installation * **Problem:** When you double-click on the Zettelstore executable icon, macOS complains that Zettelstore is an application from an unknown developer. Therefore, it will not start Zettelstore. ** **Solution:** Press the ''Ctrl'' key while opening the context menu of the Zettelstore executable with a right-click. |
︙ | ︙ | |||
24 25 26 27 28 29 30 | The difference between these two is the missing encryption of user name / password and for the answer of the Zettelstore if you use the ''http://'' schema. To be secure by default, the Zettelstore will not work in an insecure environment. ** **Solution 1:** If you are sure that your communication medium is safe, even if you use the ''http:/\/'' schema (for example, you are running the Zettelstore on the same computer you are working on, or if the Zettelstore is running on a computer in your protected local network), then you could add the entry ''insecure-cookie: true'' in you [[startup configuration|00001004010000#insecure-cookie]] file. ** **Solution 2:** If you are not sure about the security of your communication medium (for example, if unknown persons might use your local network), then you should run an [[external server|00001010090100]] in front of your Zettelstore to enable the use of the ''https://'' schema. === Working with Zettel Files * **Problem:** When you delete a zettel file by removing it from the ""disk"", e.g. by dropping it into the trash folder, by dragging into another folder, or by removing it from the command line, Zettelstore sometimes did not detect that change. | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | The difference between these two is the missing encryption of user name / password and for the answer of the Zettelstore if you use the ''http://'' schema. To be secure by default, the Zettelstore will not work in an insecure environment. ** **Solution 1:** If you are sure that your communication medium is safe, even if you use the ''http:/\/'' schema (for example, you are running the Zettelstore on the same computer you are working on, or if the Zettelstore is running on a computer in your protected local network), then you could add the entry ''insecure-cookie: true'' in you [[startup configuration|00001004010000#insecure-cookie]] file. ** **Solution 2:** If you are not sure about the security of your communication medium (for example, if unknown persons might use your local network), then you should run an [[external server|00001010090100]] in front of your Zettelstore to enable the use of the ''https://'' schema. === Working with Zettel Files * **Problem:** When you delete a zettel file by removing it from the ""disk"", e.g. by dropping it into the trash folder, by dragging into another folder, or by removing it from the command line, Zettelstore sometimes did not detect that change. If you access the zettel via Zettelstore, a fatal error is reported. ** **Explanation:** Sometimes, the operating system does not tell Zettelstore about the removed zettel. This occurs mostly under MacOS. ** **Solution 1:** If you are running Zettelstore in [[""simple-mode""|00001004051100]] or if you have enabled [[''expert-mode''|00001004020000#expert-mode]], you are allowed to refresh the internal data by selecting ""Refresh"" in the Web User Interface (you find it in the menu ""Lists""). ** **Solution 2:** There is an [[API|00001012080500]] call to make Zettelstore aware of this change. ** **Solution 3:** If you have an enabled [[Administrator Console|00001004100000]] you can use the command [[''refresh''|00001004101000#refresh]] to make your changes visible. ** **Solution 4:** You configure the zettel box as [[""simple""|00001004011400]]. |
︙ | ︙ |
Changes to encoder/encoder.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package encoder provides a generic interface to encode the abstract syntax // tree into some text form. package encoder import ( |
︙ | ︙ |
Changes to encoder/encoder_blob_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package encoder_test import ( "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" _ "zettelstore.de/z/parser/blob" // Allow to use BLOB parser. ) |
︙ | ︙ | |||
40 41 42 43 44 45 46 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b, 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{ encoderHTML: `<p><img alt="PNG" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="></p>`, | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b, 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{ encoderHTML: `<p><img alt="PNG" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="></p>`, encoderSz: `(BLOCK (BLOB (INLINE (TEXT "PNG")) "png" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="))`, encoderSHTML: `((p (img (@ (alt . "PNG") (src . "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg==")))))`, encoderText: "", encoderZmk: `%% Unable to display BLOB with description 'PNG' and syntax 'png'.`, }, }, } |
︙ | ︙ |
Changes to encoder/encoder_block_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package encoder_test var tcsBlock = []zmkTestCase{ { descr: "Empty Zettelmarkup should produce near nothing", |
︙ | ︙ | |||
52 53 54 55 56 57 58 | }, { descr: "Rendered block comment", zmk: "%%%{-}\nRender\n%%%", expect: expectMap{ encoderHTML: "<!--\nRender\n-->\n", encoderMD: "", | | | | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | }, { descr: "Rendered block comment", zmk: "%%%{-}\nRender\n%%%", expect: expectMap{ encoderHTML: "<!--\nRender\n-->\n", encoderMD: "", encoderSz: `(BLOCK (VERBATIM-COMMENT (quote (("-" . ""))) "Render"))`, encoderSHTML: "((@@@ \"Render\"))", encoderText: ``, encoderZmk: useZmk, }, }, { descr: "Simple Heading", zmk: `=== Top`, expect: expectMap{ encoderHTML: "<h2 id=\"top\">Top</h2>", encoderMD: "# Top", encoderSz: `(BLOCK (HEADING 1 () "top" "top" (INLINE (TEXT "Top"))))`, encoderSHTML: `((h2 (@ (id . "top")) "Top"))`, encoderText: `Top`, encoderZmk: useZmk, }, }, { descr: "Simple List", zmk: "* A\n* B\n* C", expect: expectMap{ |
︙ | ︙ | |||
124 125 126 127 128 129 130 | }, { descr: "Thematic break with attribute", zmk: `---{lang="zmk"}`, expect: expectMap{ encoderHTML: `<hr lang="zmk">`, encoderMD: "---", | | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | }, { descr: "Thematic break with attribute", zmk: `---{lang="zmk"}`, expect: expectMap{ encoderHTML: `<hr lang="zmk">`, encoderMD: "---", encoderSz: `(BLOCK (THEMATIC (quote (("lang" . "zmk")))))`, encoderSHTML: `((hr (@ (lang . "zmk"))))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "No list after paragraph", |
︙ | ︙ | |||
158 159 160 161 162 163 164 | encoderZmk: useZmk, }, }, { descr: "Simple List Quote", zmk: "> ToBeOrNotToBe", expect: expectMap{ | | | | | | | | | | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | encoderZmk: useZmk, }, }, { descr: "Simple List Quote", zmk: "> ToBeOrNotToBe", expect: expectMap{ encoderHTML: "<blockquote><p>ToBeOrNotToBe</p></blockquote>", encoderMD: "> ToBeOrNotToBe", encoderSz: `(BLOCK (QUOTATION (INLINE (TEXT "ToBeOrNotToBe"))))`, encoderSHTML: `((blockquote (p "ToBeOrNotToBe")))`, encoderText: "ToBeOrNotToBe", encoderZmk: useZmk, }, }, { descr: "Simple Quote Block", zmk: "<<<\nToBeOrNotToBe\n<<< Romeo", expect: expectMap{ encoderHTML: "<blockquote><p>ToBeOrNotToBe</p><cite>Romeo</cite></blockquote>", encoderMD: "> ToBeOrNotToBe", encoderSz: `(BLOCK (REGION-QUOTE () (BLOCK (PARA (TEXT "ToBeOrNotToBe"))) (INLINE (TEXT "Romeo"))))`, encoderSHTML: `((blockquote (p "ToBeOrNotToBe") (cite "Romeo")))`, encoderText: "ToBeOrNotToBe\nRomeo", encoderZmk: useZmk, }, }, { descr: "Quote Block with multiple paragraphs", zmk: "<<<\nToBeOr\n\nNotToBe\n<<< Romeo", expect: expectMap{ encoderHTML: "<blockquote><p>ToBeOr</p><p>NotToBe</p><cite>Romeo</cite></blockquote>", encoderMD: "> ToBeOr\n\n> NotToBe", encoderSz: `(BLOCK (REGION-QUOTE () (BLOCK (PARA (TEXT "ToBeOr")) (PARA (TEXT "NotToBe"))) (INLINE (TEXT "Romeo"))))`, encoderSHTML: `((blockquote (p "ToBeOr") (p "NotToBe") (cite "Romeo")))`, encoderText: "ToBeOr\nNotToBe\nRomeo", encoderZmk: useZmk, }, }, { descr: "Verse block", zmk: `""" A line another line Back Paragraph Spacy Para """ Author`, expect: expectMap{ encoderHTML: "<div><p>A\u00a0line<br>\u00a0\u00a0another\u00a0line<br>Back</p><p>Paragraph</p><p>\u00a0\u00a0\u00a0\u00a0Spacy\u00a0\u00a0Para</p><cite>Author</cite></div>", encoderMD: "", encoderSz: "(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\")))", 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", }, }, { descr: "Span Block", zmk: `::: A simple span and much more :::`, expect: expectMap{ encoderHTML: "<div><p>A simple span and much more</p></div>", encoderMD: "", encoderSz: `(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")))`, encoderText: `A simple span and much more`, encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Code", |
︙ | ︙ | |||
244 245 246 247 248 249 250 | }, { descr: "Simple Verbatim Code with visible spaces", zmk: "```{-}\nHello World\n```", expect: expectMap{ encoderHTML: "<pre><code>Hello\u2423World</code></pre>", encoderMD: " Hello World", | | | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | }, { descr: "Simple Verbatim Code with visible spaces", zmk: "```{-}\nHello World\n```", expect: expectMap{ encoderHTML: "<pre><code>Hello\u2423World</code></pre>", encoderMD: " Hello World", encoderSz: `(BLOCK (VERBATIM-CODE (quote (("-" . ""))) "Hello World"))`, encoderSHTML: "((pre (code \"Hello\u2423World\")))", encoderText: "Hello World", encoderZmk: useZmk, }, }, { descr: "Simple Verbatim Eval", |
︙ | ︙ | |||
280 281 282 283 284 285 286 | }, { descr: "Simple Description List", zmk: "; Zettel\n: Paper\n: Note\n; Zettelkasten\n: Slip box", expect: expectMap{ encoderHTML: "<dl><dt>Zettel</dt><dd><p>Paper</p></dd><dd><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>", encoderMD: "", | | | | | | | | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | }, { descr: "Simple Description List", zmk: "; Zettel\n: Paper\n: Note\n; Zettelkasten\n: Slip box", expect: expectMap{ encoderHTML: "<dl><dt>Zettel</dt><dd><p>Paper</p></dd><dd><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>", encoderMD: "", encoderSz: `(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: "<dl><dt>Zettel</dt><dd><p>Paper</p><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>", encoderMD: "", encoderSz: `(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"))))`, encoderText: "Zettel\nPaper\nNote\nZettelkasten\nSlip box", encoderZmk: useZmk, }, }, { descr: "Description List with keys, but no descriptions", zmk: "; K1\n: D11\n: D12\n; K2\n; K3\n: D31", expect: expectMap{ encoderHTML: "<dl><dt>K1</dt><dd><p>D11</p></dd><dd><p>D12</p></dd><dt>K2</dt><dt>K3</dt><dd><p>D31</p></dd></dl>", encoderMD: "", encoderSz: `(BLOCK (DESCRIPTION (INLINE (TEXT "K1")) (BLOCK (BLOCK (PARA (TEXT "D11"))) (BLOCK (PARA (TEXT "D12")))) (INLINE (TEXT "K2")) (BLOCK) (INLINE (TEXT "K3")) (BLOCK (BLOCK (PARA (TEXT "D31"))))))`, encoderSHTML: `((dl (dt "K1") (dd (p "D11")) (dd (p "D12")) (dt "K2") (dt "K3") (dd (p "D31"))))`, encoderText: "K1\nD11\nD12\nK2\nK3\nD31", encoderZmk: useZmk, }, }, { descr: "Simple Table", zmk: "|c1|c2|c3\n|d1||d3", expect: expectMap{ encoderHTML: `<table><tbody><tr><td>c1</td><td>c2</td><td>c3</td></tr><tr><td>d1</td><td></td><td>d3</td></tr></tbody></table>`, encoderMD: "", encoderSz: `(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")))))`, encoderText: "c1 c2 c3\nd1 d3", encoderZmk: useZmk, }, }, { descr: "Table with alignment and comment", zmk: `|h1>|=h2|h3:| |%--+---+---+ |<c1|c2|:c3| |f1|f2|=f3`, expect: expectMap{ encoderHTML: `<table><thead><tr><td class="right">h1</td><td>h2</td><td class="center">h3</td></tr></thead><tbody><tr><td class="left">c1</td><td>c2</td><td class="center">c3</td></tr><tr><td class="right">f1</td><td>f2</td><td class="center">=f3</td></tr></tbody></table>`, encoderMD: "", encoderSz: `(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")))))`, encoderText: "h1 h2 h3\nc1 c2 c3\nf1 f2 =f3", encoderZmk: `|=h1>|=h2|=h3: |<c1|c2|c3 |f1|f2|=f3`, }, }, { descr: "Simple Endnote", zmk: `Text[^Endnote]`, expect: expectMap{ encoderHTML: "<p>Text<sup id=\"fnref:1\"><a class=\"zs-noteref\" href=\"#fn:1\" role=\"doc-noteref\">1</a></sup></p><ol class=\"zs-endnotes\"><li class=\"zs-endnote\" id=\"fn:1\" role=\"doc-endnote\" value=\"1\">Endnote <a class=\"zs-endnote-backref\" href=\"#fnref:1\" role=\"doc-backlink\">\u21a9\ufe0e</a></li></ol>", encoderMD: "Text", encoderSz: `(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: "<p>Text<sup id=\"fnref:1\"><a class=\"zs-noteref\" href=\"#fn:1\" role=\"doc-noteref\">1</a></sup></p><ol class=\"zs-endnotes\"><li class=\"zs-endnote\" id=\"fn:1\" role=\"doc-endnote\" value=\"1\">Endnote<sup id=\"fnref:2\"><a class=\"zs-noteref\" href=\"#fn:2\" role=\"doc-noteref\">2</a></sup> <a class=\"zs-endnote-backref\" href=\"#fnref:1\" role=\"doc-backlink\">\u21a9\ufe0e</a></li><li class=\"zs-endnote\" id=\"fn:2\" role=\"doc-endnote\" value=\"2\">Nested <a class=\"zs-endnote-backref\" href=\"#fnref:2\" role=\"doc-backlink\">\u21a9\ufe0e</a></li></ol>", encoderMD: "Text", encoderSz: `(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", encoderZmk: useZmk, }, }, { descr: "Transclusion", zmk: `{{{http://example.com/image}}}{width="100px"}`, expect: expectMap{ encoderHTML: `<p><img class="external" src="http://example.com/image" width="100px"></p>`, encoderMD: "", encoderSz: `(BLOCK (TRANSCLUDE (quote (("width" . "100px"))) (quote (EXTERNAL "http://example.com/image"))))`, encoderSHTML: `((p (img (@ (class . "external") (src . "http://example.com/image") (width . "100px")))))`, encoderText: "", encoderZmk: useZmk, }, }, { descr: "A paragraph with a inline comment only should be empty in HTML", |
︙ | ︙ |
Changes to encoder/encoder_inline_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package encoder_test var tcsInline = []zmkTestCase{ { descr: "Empty Zettelmarkup should produce near nothing (inline)", |
︙ | ︙ | |||
160 161 162 163 164 165 166 | }, { descr: "Quotes formatting (german)", zmk: `""quotes""{lang=de}`, expect: expectMap{ encoderHTML: `<span lang="de">„quotes“</span>`, encoderMD: "<q>quotes</q>", | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | }, { descr: "Quotes formatting (german)", zmk: `""quotes""{lang=de}`, expect: expectMap{ encoderHTML: `<span lang="de">„quotes“</span>`, encoderMD: "<q>quotes</q>", encoderSz: `(INLINE (FORMAT-QUOTE (quote (("lang" . "de"))) (TEXT "quotes")))`, encoderSHTML: `((span (@ (lang . "de")) (@H "„") "quotes" (@H "“")))`, encoderText: `quotes`, encoderZmk: `""quotes""{lang="de"}`, }, }, { descr: "Empty quotes (default)", |
︙ | ︙ | |||
184 185 186 187 188 189 190 | }, { descr: "Empty quotes (unknown)", zmk: `""""{lang=unknown}`, expect: expectMap{ encoderHTML: `<span lang="unknown">""</span>`, encoderMD: "<q></q>", | | | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | }, { descr: "Empty quotes (unknown)", zmk: `""""{lang=unknown}`, expect: expectMap{ encoderHTML: `<span lang="unknown">""</span>`, encoderMD: "<q></q>", encoderSz: `(INLINE (FORMAT-QUOTE (quote (("lang" . "unknown")))))`, encoderSHTML: `((span (@ (lang . "unknown")) (@H """ """)))`, encoderText: ``, encoderZmk: `""""{lang="unknown"}`, }, }, { descr: "Nested quotes (default)", |
︙ | ︙ | |||
256 257 258 259 260 261 262 | }, { descr: "Code formatting with visible space", zmk: "``x y``{-}", expect: expectMap{ encoderHTML: "<code>x\u2423y</code>", encoderMD: "`x y`", | | | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | }, { descr: "Code formatting with visible space", zmk: "``x y``{-}", expect: expectMap{ encoderHTML: "<code>x\u2423y</code>", encoderMD: "`x y`", encoderSz: `(INLINE (LITERAL-CODE (quote (("-" . ""))) "x y"))`, encoderSHTML: "((code \"x\u2423y\"))", encoderText: `x y`, encoderZmk: useZmk, }, }, { descr: "HTML in Code formatting", |
︙ | ︙ | |||
316 317 318 319 320 321 322 | }, { descr: "Nested Span Quote formatting", zmk: `::""abc""::{lang=fr}`, expect: expectMap{ encoderHTML: `<span lang="fr">« abc »</span>`, encoderMD: "<q>abc</q>", | | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | }, { descr: "Nested Span Quote formatting", zmk: `::""abc""::{lang=fr}`, expect: expectMap{ encoderHTML: `<span lang="fr">« abc »</span>`, encoderMD: "<q>abc</q>", encoderSz: `(INLINE (FORMAT-SPAN (quote (("lang" . "fr"))) (FORMAT-QUOTE () (TEXT "abc"))))`, encoderSHTML: `((span (@ (lang . "fr")) (@L (@H "«" " ") "abc" (@H " " "»"))))`, encoderText: `abc`, encoderZmk: `::""abc""::{lang="fr"}`, }, }, { descr: "Simple Citation", |
︙ | ︙ | |||
375 376 377 378 379 380 381 | }, { descr: "Line comment", zmk: `%%{-} line comment`, expect: expectMap{ encoderHTML: `<!-- line comment -->`, encoderMD: "", | | | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | }, { descr: "Line comment", zmk: `%%{-} line comment`, expect: expectMap{ encoderHTML: `<!-- line comment -->`, encoderMD: "", encoderSz: `(INLINE (LITERAL-COMMENT (quote (("-" . ""))) "line comment"))`, encoderSHTML: `((@@ "line comment"))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "Comment after text", zmk: `Text %%{-} comment`, expect: expectMap{ encoderHTML: `Text<!-- comment -->`, encoderMD: "Text", encoderSz: `(INLINE (TEXT "Text") (LITERAL-COMMENT (quote (("-" . ""))) "comment"))`, encoderSHTML: `("Text" (@@ "comment"))`, encoderText: `Text`, encoderZmk: useZmk, }, }, { descr: "Comment after text and with -->", zmk: `Text %%{-} comment --> end`, expect: expectMap{ encoderHTML: `Text<!-- comment --> end -->`, encoderMD: "Text", encoderSz: `(INLINE (TEXT "Text") (LITERAL-COMMENT (quote (("-" . ""))) "comment --> end"))`, encoderSHTML: `("Text" (@@ "comment --> end"))`, encoderText: `Text`, encoderZmk: useZmk, }, }, { descr: "Simple inline endnote", zmk: `[^endnote]`, expect: expectMap{ encoderHTML: `<sup id="fnref:1"><a class="zs-noteref" href="#fn:1" role="doc-noteref">1</a></sup>`, encoderMD: "", encoderSz: `(INLINE (ENDNOTE () (quote (INLINE (TEXT "endnote")))))`, encoderSHTML: `((sup (@ (id . "fnref:1")) (a (@ (class . "zs-noteref") (href . "#fn:1") (role . "doc-noteref")) "1")))`, encoderText: `endnote`, encoderZmk: useZmk, }, }, { descr: "Simple mark", |
︙ | ︙ | |||
627 628 629 630 631 632 633 | }, { descr: "Dummy Embed", zmk: `{{abc}}`, expect: expectMap{ encoderHTML: `<img src="abc">`, encoderMD: "![abc](abc)", | | | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 | }, { descr: "Dummy Embed", zmk: `{{abc}}`, expect: expectMap{ encoderHTML: `<img src="abc">`, encoderMD: "![abc](abc)", encoderSz: `(INLINE (EMBED () (quote (EXTERNAL "abc")) ""))`, encoderSHTML: `((img (@ (src . "abc"))))`, encoderText: ``, encoderZmk: useZmk, }, }, { descr: "Inline HTML Zettel", |
︙ | ︙ | |||
651 652 653 654 655 656 657 | }, { descr: "Inline Text Zettel", zmk: `@@<hr>@@{="text"}`, expect: expectMap{ encoderHTML: ``, encoderMD: "<hr>", | | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | }, { descr: "Inline Text Zettel", zmk: `@@<hr>@@{="text"}`, expect: expectMap{ encoderHTML: ``, encoderMD: "<hr>", encoderSz: `(INLINE (LITERAL-ZETTEL (quote (("" . "text"))) "<hr>"))`, encoderSHTML: `(())`, encoderText: `<hr>`, encoderZmk: useZmk, }, }, { descr: "", |
︙ | ︙ |
Changes to encoder/encoder_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package encoder_test import ( "fmt" "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/encoder" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" _ "zettelstore.de/z/encoder/htmlenc" // Allow to use HTML encoder. _ "zettelstore.de/z/encoder/mdenc" // Allow to use markdown encoder. _ "zettelstore.de/z/encoder/shtmlenc" // Allow to use SHTML encoder. _ "zettelstore.de/z/encoder/szenc" // Allow to use sz encoder. |
︙ | ︙ | |||
115 116 117 118 119 120 121 | return } val, err := sxreader.MakeReader(strings.NewReader(exp)).Read() if err != nil { t.Error(err) return } | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | return } val, err := sxreader.MakeReader(strings.NewReader(exp)).Read() if err != nil { t.Error(err) return } got := val.Repr() if exp != got { prefix := fmt.Sprintf("Test #%d", testNum) if d := descr; d != "" { prefix += "\nReason: " + d } prefix += "\nMode: " + pe.mode() t.Errorf("%s\n\nExpected: %q\nGot: %q", prefix, exp, got) |
︙ | ︙ |
Changes to encoder/htmlenc/htmlenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package htmlenc encodes the abstract syntax tree into HTML5 via zettelstore-client. package htmlenc import ( "io" |
︙ | ︙ | |||
36 37 38 39 40 41 42 | // Create an encoder. func Create(params *encoder.CreateParameter) *Encoder { // We need a new transformer every time, because tx.inVerse must be unique. // If we can refactor it out, the transformer can be created only once. return &Encoder{ tx: szenc.NewTransformer(), | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | // Create an encoder. func Create(params *encoder.CreateParameter) *Encoder { // We need a new transformer every time, because tx.inVerse must be unique. // If we can refactor it out, the transformer can be created only once. return &Encoder{ tx: szenc.NewTransformer(), th: shtml.NewEvaluator(1, nil), lang: params.Lang, textEnc: textenc.Create(), } } type Encoder struct { tx *szenc.Transformer |
︙ | ︙ | |||
76 77 78 79 80 81 82 | xast := he.tx.GetSz(&zn.Ast) hast, err := he.th.Evaluate(xast, &env) if err != nil { return 0, err } hen := he.th.Endnotes(&env) | > > | > | | > | > | | | | > | > | | | | | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | xast := he.tx.GetSz(&zn.Ast) hast, err := he.th.Evaluate(xast, &env) if err != nil { return 0, err } hen := he.th.Endnotes(&env) sf := he.th.SymbolFactory() symAttr := sf.MustMake(sxhtml.NameSymAttr) head := sx.MakeList(sf.MustMake("head")) curr := head curr = curr.AppendBang(sx.Nil().Cons(sx.Nil().Cons(sx.Cons(sf.MustMake("charset"), sx.String("utf-8"))).Cons(symAttr)).Cons(sf.MustMake("meta"))) for elem := hm; elem != nil; elem = elem.Tail() { curr = curr.AppendBang(elem.Car()) } var sb strings.Builder if hasTitle { he.textEnc.WriteInlines(&sb, &isTitle) } else { sb.Write(zn.Meta.Zid.Bytes()) } _ = curr.AppendBang(sx.Nil().Cons(sx.String(sb.String())).Cons(sf.MustMake("title"))) body := sx.MakeList(sf.MustMake("body")) curr = body if hasTitle { curr = curr.AppendBang(htitle.Cons(sf.MustMake("h1"))) } for elem := hast; elem != nil; elem = elem.Tail() { curr = curr.AppendBang(elem.Car()) } if hen != nil { curr = curr.AppendBang(sx.Nil().Cons(sf.MustMake("hr"))) _ = curr.AppendBang(hen) } doc := sx.MakeList( sf.MustMake(sxhtml.NameSymDoctype), sx.MakeList(sf.MustMake("html"), head, body), ) gen := sxhtml.NewGenerator(sf, sxhtml.WithNewline) return gen.WriteHTML(w, doc) } // WriteMeta encodes meta data as HTML5. func (he *Encoder) WriteMeta(w io.Writer, m *meta.Meta, evalMeta encoder.EvalMetaFunc) (int, error) { env := shtml.MakeEnvironment(he.lang) hm, err := he.th.Evaluate(he.tx.GetMeta(m, evalMeta), &env) if err != nil { return 0, err } gen := sxhtml.NewGenerator(he.th.SymbolFactory(), sxhtml.WithNewline) return gen.WriteListHTML(w, hm) } func (he *Encoder) WriteContent(w io.Writer, zn *ast.ZettelNode) (int, error) { return he.WriteBlocks(w, &zn.Ast) } // WriteBlocks encodes a block slice. func (he *Encoder) WriteBlocks(w io.Writer, bs *ast.BlockSlice) (int, error) { env := shtml.MakeEnvironment(he.lang) hobj, err := he.th.Evaluate(he.tx.GetSz(bs), &env) if err == nil { gen := sxhtml.NewGenerator(he.th.SymbolFactory()) length, err2 := gen.WriteListHTML(w, hobj) if err2 != nil { return length, err2 } l, err2 := gen.WriteHTML(w, he.th.Endnotes(&env)) length += l return length, err2 } return 0, err } // WriteInlines writes an inline slice to the writer func (he *Encoder) WriteInlines(w io.Writer, is *ast.InlineSlice) (int, error) { env := shtml.MakeEnvironment(he.lang) hobj, err := he.th.Evaluate(he.tx.GetSz(is), &env) if err == nil { gen := sxhtml.NewGenerator(sx.FindSymbolFactory(hobj)) length, err2 := gen.WriteListHTML(w, hobj) if err2 != nil { return length, err2 } return length, nil } return 0, err } |
Changes to encoder/mdenc/mdenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package mdenc encodes the abstract syntax tree back into Markdown. package mdenc import ( "io" |
︙ | ︙ | |||
352 353 354 355 356 357 358 | case ast.LiteralComment, ast.LiteralHTML: // ignore everything default: v.b.Write(ln.Content) } } func (v *visitor) writeSpaces(n int) { | | | 349 350 351 352 353 354 355 356 357 358 359 | case ast.LiteralComment, ast.LiteralHTML: // ignore everything default: v.b.Write(ln.Content) } } func (v *visitor) writeSpaces(n int) { for i := 0; i < n; i++ { v.b.WriteByte(' ') } } |
Changes to encoder/shtmlenc/shtmlenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- // Package shtmlenc encodes the abstract syntax tree into a s-expr which represents HTML. package shtmlenc import ( "io" |
︙ | ︙ | |||
32 33 34 35 36 37 38 | // Create a SHTML encoder func Create(params *encoder.CreateParameter) *Encoder { // We need a new transformer every time, because tx.inVerse must be unique. // If we can refactor it out, the transformer can be created only once. return &Encoder{ tx: szenc.NewTransformer(), | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // Create a SHTML encoder func Create(params *encoder.CreateParameter) *Encoder { // We need a new transformer every time, because tx.inVerse must be unique. // If we can refactor it out, the transformer can be created only once. return &Encoder{ tx: szenc.NewTransformer(), th: shtml.NewEvaluator(1, nil), lang: params.Lang, } } type Encoder struct { tx *szenc.Transformer th *shtml.Evaluator |
︙ | ︙ |
Changes to encoder/szenc/szenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package szenc encodes the abstract syntax tree into a s-expr for zettel. package szenc import ( "io" |
︙ | ︙ |
Changes to encoder/szenc/transform.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | > | < | < < < < | | | | | | | | | | | | | | | | | < < < < < < < | | | < < < < < < < < < < < < | | | | | | | | | | | | | | < < < < < < < < < < < < | | < > > > > > | < < < < < < < < < < < < | | | | | > | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package szenc import ( "encoding/base64" "fmt" "strings" "zettelstore.de/client.fossil/attrs" "zettelstore.de/client.fossil/sz" "zettelstore.de/sx.fossil" "zettelstore.de/z/ast" "zettelstore.de/z/encoder" "zettelstore.de/z/zettel/meta" ) // NewTransformer returns a new transformer to create s-expressions from AST nodes. func NewTransformer() *Transformer { sf := sx.MakeMappedFactory(1024) t := Transformer{sf: sf} t.zetSyms.InitializeZettelSymbols(sf) t.mapVerbatimKindS = map[ast.VerbatimKind]*sx.Symbol{ ast.VerbatimZettel: t.zetSyms.SymVerbatimZettel, ast.VerbatimProg: t.zetSyms.SymVerbatimProg, ast.VerbatimEval: t.zetSyms.SymVerbatimEval, ast.VerbatimMath: t.zetSyms.SymVerbatimMath, ast.VerbatimComment: t.zetSyms.SymVerbatimComment, ast.VerbatimHTML: t.zetSyms.SymVerbatimHTML, } t.mapRegionKindS = map[ast.RegionKind]*sx.Symbol{ ast.RegionSpan: t.zetSyms.SymRegionBlock, ast.RegionQuote: t.zetSyms.SymRegionQuote, ast.RegionVerse: t.zetSyms.SymRegionVerse, } t.mapNestedListKindS = map[ast.NestedListKind]*sx.Symbol{ ast.NestedListOrdered: t.zetSyms.SymListOrdered, ast.NestedListUnordered: t.zetSyms.SymListUnordered, ast.NestedListQuote: t.zetSyms.SymListQuote, } t.alignmentSymbolS = map[ast.Alignment]*sx.Symbol{ ast.AlignDefault: t.zetSyms.SymCell, ast.AlignLeft: t.zetSyms.SymCellLeft, ast.AlignCenter: t.zetSyms.SymCellCenter, ast.AlignRight: t.zetSyms.SymCellRight, } t.mapRefStateLink = map[ast.RefState]*sx.Symbol{ ast.RefStateInvalid: t.zetSyms.SymLinkInvalid, ast.RefStateZettel: t.zetSyms.SymLinkZettel, ast.RefStateSelf: t.zetSyms.SymLinkSelf, ast.RefStateFound: t.zetSyms.SymLinkFound, ast.RefStateBroken: t.zetSyms.SymLinkBroken, ast.RefStateHosted: t.zetSyms.SymLinkHosted, ast.RefStateBased: t.zetSyms.SymLinkBased, ast.RefStateQuery: t.zetSyms.SymLinkQuery, ast.RefStateExternal: t.zetSyms.SymLinkExternal, } t.mapFormatKindS = map[ast.FormatKind]*sx.Symbol{ ast.FormatEmph: t.zetSyms.SymFormatEmph, ast.FormatStrong: t.zetSyms.SymFormatStrong, ast.FormatDelete: t.zetSyms.SymFormatDelete, ast.FormatInsert: t.zetSyms.SymFormatInsert, ast.FormatSuper: t.zetSyms.SymFormatSuper, ast.FormatSub: t.zetSyms.SymFormatSub, ast.FormatQuote: t.zetSyms.SymFormatQuote, ast.FormatMark: t.zetSyms.SymFormatMark, ast.FormatSpan: t.zetSyms.SymFormatSpan, } t.mapLiteralKindS = map[ast.LiteralKind]*sx.Symbol{ ast.LiteralZettel: t.zetSyms.SymLiteralZettel, ast.LiteralProg: t.zetSyms.SymLiteralProg, ast.LiteralInput: t.zetSyms.SymLiteralInput, ast.LiteralOutput: t.zetSyms.SymLiteralOutput, ast.LiteralComment: t.zetSyms.SymLiteralComment, ast.LiteralHTML: t.zetSyms.SymLiteralHTML, ast.LiteralMath: t.zetSyms.SymLiteralMath, } t.mapRefStateS = map[ast.RefState]*sx.Symbol{ ast.RefStateInvalid: t.zetSyms.SymRefStateInvalid, ast.RefStateZettel: t.zetSyms.SymRefStateZettel, ast.RefStateSelf: t.zetSyms.SymRefStateSelf, ast.RefStateFound: t.zetSyms.SymRefStateFound, ast.RefStateBroken: t.zetSyms.SymRefStateBroken, ast.RefStateHosted: t.zetSyms.SymRefStateHosted, ast.RefStateBased: t.zetSyms.SymRefStateBased, ast.RefStateQuery: t.zetSyms.SymRefStateQuery, ast.RefStateExternal: t.zetSyms.SymRefStateExternal, } t.mapMetaTypeS = map[*meta.DescriptionType]*sx.Symbol{ meta.TypeCredential: t.zetSyms.SymTypeCredential, meta.TypeEmpty: t.zetSyms.SymTypeEmpty, meta.TypeID: t.zetSyms.SymTypeID, meta.TypeIDSet: t.zetSyms.SymTypeIDSet, meta.TypeNumber: t.zetSyms.SymTypeNumber, meta.TypeString: t.zetSyms.SymTypeString, meta.TypeTagSet: t.zetSyms.SymTypeTagSet, meta.TypeTimestamp: t.zetSyms.SymTypeTimestamp, meta.TypeURL: t.zetSyms.SymTypeURL, meta.TypeWord: t.zetSyms.SymTypeWord, meta.TypeWordSet: t.zetSyms.SymTypeWordSet, meta.TypeZettelmarkup: t.zetSyms.SymTypeZettelmarkup, } return &t } type Transformer struct { sf sx.SymbolFactory zetSyms sz.ZettelSymbols mapVerbatimKindS map[ast.VerbatimKind]*sx.Symbol mapRegionKindS map[ast.RegionKind]*sx.Symbol mapNestedListKindS map[ast.NestedListKind]*sx.Symbol alignmentSymbolS map[ast.Alignment]*sx.Symbol mapRefStateLink map[ast.RefState]*sx.Symbol mapFormatKindS map[ast.FormatKind]*sx.Symbol mapLiteralKindS map[ast.LiteralKind]*sx.Symbol mapRefStateS map[ast.RefState]*sx.Symbol mapMetaTypeS map[*meta.DescriptionType]*sx.Symbol inVerse bool } func (t *Transformer) GetSz(node ast.Node) *sx.Pair { switch n := node.(type) { case *ast.BlockSlice: return t.getBlockSlice(n) case *ast.InlineSlice: return t.getInlineSlice(*n) case *ast.ParaNode: return t.getInlineSlice(n.Inlines).Tail().Cons(t.zetSyms.SymPara) case *ast.VerbatimNode: return sx.MakeList( mapGetS(t, t.mapVerbatimKindS, n.Kind), t.getAttributes(n.Attrs), sx.String(string(n.Content)), ) case *ast.RegionNode: return t.getRegion(n) case *ast.HeadingNode: return sx.MakeList( t.zetSyms.SymHeading, sx.Int64(int64(n.Level)), t.getAttributes(n.Attrs), sx.String(n.Slug), sx.String(n.Fragment), t.getInlineSlice(n.Inlines), ) case *ast.HRuleNode: return sx.MakeList(t.zetSyms.SymThematic, t.getAttributes(n.Attrs)) case *ast.NestedListNode: return t.getNestedList(n) case *ast.DescriptionListNode: return t.getDescriptionList(n) case *ast.TableNode: return t.getTable(n) case *ast.TranscludeNode: return sx.MakeList(t.zetSyms.SymTransclude, t.getAttributes(n.Attrs), t.getReference(n.Ref)) case *ast.BLOBNode: return t.getBLOB(n) case *ast.TextNode: return sx.MakeList(t.zetSyms.SymText, sx.String(n.Text)) case *ast.SpaceNode: if t.inVerse { return sx.MakeList(t.zetSyms.SymSpace, sx.String(n.Lexeme)) } return sx.MakeList(t.zetSyms.SymSpace) case *ast.BreakNode: if n.Hard { return sx.MakeList(t.zetSyms.SymHard) } return sx.MakeList(t.zetSyms.SymSoft) case *ast.LinkNode: return t.getLink(n) case *ast.EmbedRefNode: return t.getInlineSlice(n.Inlines).Tail(). Cons(sx.String(n.Syntax)). Cons(t.getReference(n.Ref)). Cons(t.getAttributes(n.Attrs)). Cons(t.zetSyms.SymEmbed) case *ast.EmbedBLOBNode: return t.getEmbedBLOB(n) case *ast.CiteNode: return t.getInlineSlice(n.Inlines).Tail(). Cons(sx.String(n.Key)). Cons(t.getAttributes(n.Attrs)). Cons(t.zetSyms.SymCite) case *ast.FootnoteNode: text := sx.Nil().Cons(sx.Nil().Cons(t.getInlineSlice(n.Inlines)).Cons(t.zetSyms.SymQuote)) return text.Cons(t.getAttributes(n.Attrs)).Cons(t.zetSyms.SymEndnote) case *ast.MarkNode: return t.getInlineSlice(n.Inlines).Tail(). Cons(sx.String(n.Fragment)). Cons(sx.String(n.Slug)). Cons(sx.String(n.Mark)). Cons(t.zetSyms.SymMark) case *ast.FormatNode: return t.getInlineSlice(n.Inlines).Tail(). Cons(t.getAttributes(n.Attrs)). Cons(mapGetS(t, t.mapFormatKindS, n.Kind)) case *ast.LiteralNode: return sx.MakeList( mapGetS(t, t.mapLiteralKindS, n.Kind), t.getAttributes(n.Attrs), sx.String(string(n.Content)), ) } return sx.MakeList(t.zetSyms.SymUnknown, sx.String(fmt.Sprintf("%T %v", node, node))) } func (t *Transformer) getRegion(rn *ast.RegionNode) *sx.Pair { saveInVerse := t.inVerse if rn.Kind == ast.RegionVerse { t.inVerse = true } symBlocks := t.GetSz(&rn.Blocks) t.inVerse = saveInVerse return sx.MakeList( mapGetS(t, t.mapRegionKindS, rn.Kind), t.getAttributes(rn.Attrs), symBlocks, t.GetSz(&rn.Inlines), ) } func (t *Transformer) getNestedList(ln *ast.NestedListNode) *sx.Pair { nlistObjs := make([]sx.Object, len(ln.Items)+1) nlistObjs[0] = mapGetS(t, t.mapNestedListKindS, ln.Kind) isCompact := isCompactList(ln.Items) for i, item := range ln.Items { if isCompact && len(item) > 0 { paragraph := t.GetSz(item[0]) nlistObjs[i+1] = paragraph.Tail().Cons(t.zetSyms.SymInline) continue } itemObjs := make([]sx.Object, len(item)) for j, in := range item { itemObjs[j] = t.GetSz(in) } if isCompact { nlistObjs[i+1] = sx.MakeList(itemObjs...).Cons(t.zetSyms.SymInline) } else { nlistObjs[i+1] = sx.MakeList(itemObjs...).Cons(t.zetSyms.SymBlock) } } return sx.MakeList(nlistObjs...) } func isCompactList(itemSlice []ast.ItemSlice) bool { for _, items := range itemSlice { if len(items) > 1 { return false } if len(items) == 1 { if _, ok := items[0].(*ast.ParaNode); !ok { return false } } } return true } func (t *Transformer) getDescriptionList(dn *ast.DescriptionListNode) *sx.Pair { dlObjs := make([]sx.Object, 2*len(dn.Descriptions)+1) dlObjs[0] = t.zetSyms.SymDescription for i, def := range dn.Descriptions { dlObjs[2*i+1] = t.getInlineSlice(def.Term) descObjs := make([]sx.Object, len(def.Descriptions)) for j, b := range def.Descriptions { dVal := make([]sx.Object, len(b)) for k, dn := range b { dVal[k] = t.GetSz(dn) } descObjs[j] = sx.MakeList(dVal...).Cons(t.zetSyms.SymBlock) } dlObjs[2*i+2] = sx.MakeList(descObjs...).Cons(t.zetSyms.SymBlock) } return sx.MakeList(dlObjs...) } func (t *Transformer) getTable(tn *ast.TableNode) *sx.Pair { tObjs := make([]sx.Object, len(tn.Rows)+2) tObjs[0] = t.zetSyms.SymTable tObjs[1] = t.getHeader(tn.Header) for i, row := range tn.Rows { tObjs[i+2] = t.getRow(row) } return sx.MakeList(tObjs...) } func (t *Transformer) getHeader(header ast.TableRow) *sx.Pair { if len(header) == 0 { return nil } return t.getRow(header) } func (t *Transformer) getRow(row ast.TableRow) *sx.Pair { rObjs := make([]sx.Object, len(row)) for i, cell := range row { rObjs[i] = t.getCell(cell) } return sx.MakeList(rObjs...).Cons(t.zetSyms.SymList) } func (t *Transformer) getCell(cell *ast.TableCell) *sx.Pair { return t.getInlineSlice(cell.Inlines).Tail().Cons(mapGetS(t, t.alignmentSymbolS, cell.Align)) } func (t *Transformer) getBLOB(bn *ast.BLOBNode) *sx.Pair { var lastObj sx.Object if bn.Syntax == meta.SyntaxSVG { lastObj = sx.String(string(bn.Blob)) } else { lastObj = getBase64String(bn.Blob) } return sx.MakeList( t.zetSyms.SymBLOB, t.getInlineSlice(bn.Description), sx.String(bn.Syntax), lastObj, ) } func (t *Transformer) getLink(ln *ast.LinkNode) *sx.Pair { return t.getInlineSlice(ln.Inlines).Tail(). Cons(sx.String(ln.Ref.Value)). Cons(t.getAttributes(ln.Attrs)). Cons(mapGetS(t, t.mapRefStateLink, ln.Ref.State)) } func (t *Transformer) getEmbedBLOB(en *ast.EmbedBLOBNode) *sx.Pair { tail := t.getInlineSlice(en.Inlines).Tail() if en.Syntax == meta.SyntaxSVG { tail = tail.Cons(sx.String(string(en.Blob))) } else { tail = tail.Cons(getBase64String(en.Blob)) } return tail.Cons(sx.String(en.Syntax)).Cons(t.getAttributes(en.Attrs)).Cons(t.zetSyms.SymEmbedBLOB) } func (t *Transformer) getBlockSlice(bs *ast.BlockSlice) *sx.Pair { objs := make([]sx.Object, len(*bs)) for i, n := range *bs { objs[i] = t.GetSz(n) } return sx.MakeList(objs...).Cons(t.zetSyms.SymBlock) } func (t *Transformer) getInlineSlice(is ast.InlineSlice) *sx.Pair { objs := make([]sx.Object, len(is)) for i, n := range is { objs[i] = t.GetSz(n) } return sx.MakeList(objs...).Cons(t.zetSyms.SymInline) } func (t *Transformer) getAttributes(a attrs.Attributes) sx.Object { if a.IsEmpty() { return sx.Nil() } keys := a.Keys() objs := make([]sx.Object, 0, len(keys)) for _, k := range keys { objs = append(objs, sx.Cons(sx.String(k), sx.String(a[k]))) } return sx.Nil().Cons(sx.MakeList(objs...)).Cons(t.zetSyms.SymQuote) } func (t *Transformer) getReference(ref *ast.Reference) *sx.Pair { return sx.MakeList( t.zetSyms.SymQuote, sx.MakeList( mapGetS(t, t.mapRefStateS, ref.State), sx.String(ref.Value), ), ) } func (t *Transformer) GetMeta(m *meta.Meta, evalMeta encoder.EvalMetaFunc) *sx.Pair { pairs := m.ComputedPairs() objs := make([]sx.Object, 0, len(pairs)) for _, p := range pairs { key := p.Key ty := m.Type(key) symType := mapGetS(t, t.mapMetaTypeS, ty) var obj sx.Object if ty.IsSet { setList := meta.ListFromValue(p.Value) setObjs := make([]sx.Object, len(setList)) for i, val := range setList { setObjs[i] = sx.String(val) } obj = sx.MakeList(setObjs...).Cons(t.zetSyms.SymList) } else if ty == meta.TypeZettelmarkup { is := evalMeta(p.Value) obj = t.GetSz(&is) } else { obj = sx.String(p.Value) } symKey := sx.MakeList(t.zetSyms.SymQuote, t.sf.MustMake(key)) objs = append(objs, sx.Nil().Cons(obj).Cons(symKey).Cons(symType)) } return sx.MakeList(objs...).Cons(t.zetSyms.SymMeta) } func mapGetS[T comparable](t *Transformer, m map[T]*sx.Symbol, k T) *sx.Symbol { if result, found := m[k]; found { return result } return t.sf.MustMake(fmt.Sprintf("**%v:NOT-FOUND**", k)) } func getBase64String(data []byte) sx.String { var sb strings.Builder encoder := base64.NewEncoder(base64.StdEncoding, &sb) _, err := encoder.Write(data) if err == nil { |
︙ | ︙ |
Changes to encoder/textenc/textenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package textenc encodes the abstract syntax tree into its text. package textenc import ( "io" |
︙ | ︙ |
Changes to encoder/write.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package encoder import ( "encoding/base64" "io" |
︙ | ︙ |
Changes to encoder/zmkenc/zmkenc.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package zmkenc encodes the abstract syntax tree back into Zettelmarkup. package zmkenc import ( "fmt" |
︙ | ︙ | |||
371 372 373 374 375 376 377 | last = i + 1 continue } if i < len(tn.Text)-1 { s := tn.Text[i : i+2] if escapeSeqs.Has(s) { v.b.WriteString(tn.Text[last:i]) | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | last = i + 1 continue } if i < len(tn.Text)-1 { s := tn.Text[i : i+2] if escapeSeqs.Has(s) { v.b.WriteString(tn.Text[last:i]) for j := 0; j < len(s); j++ { v.b.WriteBytes('\\', s[j]) } i++ last = i + 1 continue } } |
︙ | ︙ | |||
526 527 528 529 530 531 532 | } } v.b.WriteByte('}') } func (v *visitor) writeEscaped(s string, toEscape byte) { last := 0 | | | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | } } v.b.WriteByte('}') } func (v *visitor) writeEscaped(s string, toEscape byte) { last := 0 for i := 0; i < len(s); i++ { if b := s[i]; b == toEscape || b == '\\' { v.b.WriteString(s[last:i]) v.b.WriteBytes('\\', b) last = i + 1 } } v.b.WriteString(s[last:]) } func syntaxToHTML(a attrs.Attributes) attrs.Attributes { return a.Clone().Set("", meta.SyntaxHTML).Remove(api.KeySyntax) } |
Changes to encoding/atom/atom.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package atom provides an Atom encoding. package atom import ( "bytes" |
︙ | ︙ | |||
80 81 82 83 84 85 86 | entryUpdated := "" if val, found := m.Get(api.KeyPublished); found { if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil { entryUpdated = published.UTC().Format(time.RFC3339) } } | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | entryUpdated := "" if val, found := m.Get(api.KeyPublished); found { if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil { entryUpdated = published.UTC().Format(time.RFC3339) } } link := c.NewURLBuilderAbs().SetZid(api.ZettelID(m.Zid.String())).String() buf.WriteString(" <entry>\n") xml.WriteTag(buf, " ", "title", encoding.TitleAsText(m)) xml.WriteTag(buf, " ", "id", link) buf.WriteString(` <link rel="self" href="`) strfun.XMLEscape(buf, link) buf.WriteString(`"/>` + "\n") |
︙ | ︙ |
Changes to encoding/encoding.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package encoding provides helper functions for encodings. package encoding import ( "time" |
︙ | ︙ |
Changes to encoding/rss/rss.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package rss provides a RSS encoding. package rss import ( "bytes" |
︙ | ︙ | |||
90 91 92 93 94 95 96 | itemPublished := "" if val, found := m.Get(api.KeyPublished); found { if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil { itemPublished = published.UTC().Format(time.RFC1123Z) } } | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | itemPublished := "" if val, found := m.Get(api.KeyPublished); found { if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil { itemPublished = published.UTC().Format(time.RFC1123Z) } } link := c.NewURLBuilderAbs().SetZid(api.ZettelID(m.Zid.String())).String() buf.WriteString(" <item>\n") xml.WriteTag(buf, " ", "title", encoding.TitleAsText(m)) xml.WriteTag(buf, " ", "link", link) xml.WriteTag(buf, " ", "guid", link) if itemPublished != "" { xml.WriteTag(buf, " ", "pubDate", itemPublished) |
︙ | ︙ |
Changes to encoding/xml/xml.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package xml provides helper for a XML-based encoding. package xml import ( "bytes" |
︙ | ︙ |
Changes to evaluator/evaluator.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < < < < < > < | < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package evaluator interprets and evaluates the AST. package evaluator import ( "context" "errors" "fmt" "path" "strconv" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/parser/cleaner" "zettelstore.de/z/parser/draw" "zettelstore.de/z/query" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Port contains all methods to retrieve zettel (or part of it) to evaluate a zettel. type Port interface { GetZettel(context.Context, id.Zid) (zettel.Zettel, error) QueryMeta(ctx context.Context, q *query.Query) ([]*meta.Meta, error) } // EvaluateZettel evaluates the given zettel in the given context, with the // given ports, and the given environment. func EvaluateZettel(ctx context.Context, port Port, rtConfig config.Config, zn *ast.ZettelNode) { if zn.Syntax == meta.SyntaxNone { // AST is empty, evaluate to a description list of metadata. zn.Ast = evaluateMetadata(zn.Meta) return } EvaluateBlock(ctx, port, rtConfig, &zn.Ast) } // EvaluateBlock evaluates the given block list in the given context, with // the given ports, and the given environment. func EvaluateBlock(ctx context.Context, port Port, rtConfig config.Config, bns *ast.BlockSlice) { evaluateNode(ctx, port, rtConfig, bns) cleaner.CleanBlockSlice(bns, true) |
︙ | ︙ | |||
292 293 294 295 296 297 298 | ml, err := e.port.QueryMeta(e.ctx, q) if err != nil { if errors.Is(err, &box.ErrNotAllowed{}) { return nil } return makeBlockNode(createInlineErrorText(nil, "Unable", "to", "search", "zettel")) } | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | ml, err := e.port.QueryMeta(e.ctx, q) if err != nil { if errors.Is(err, &box.ErrNotAllowed{}) { return nil } return makeBlockNode(createInlineErrorText(nil, "Unable", "to", "search", "zettel")) } result := QueryAction(e.ctx, q, ml, e.rtConfig) if result != nil { ast.Walk(e, result) } return result } func (e *evaluator) checkMaxTransclusions(ref *ast.Reference) ast.InlineNode { |
︙ | ︙ | |||
438 439 440 441 442 443 444 | if errors.Is(err, &box.ErrNotAllowed{}) { return nil } e.transcludeCount++ return createInlineErrorImage(en) } | | | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | if errors.Is(err, &box.ErrNotAllowed{}) { return nil } e.transcludeCount++ return createInlineErrorImage(en) } if syntax := zettel.Meta.GetDefault(api.KeySyntax, ""); parser.IsImageFormat(syntax) { e.updateImageRefNode(en, zettel.Meta, syntax) return en } else if !parser.IsASTParser(syntax) { // Not embeddable. e.transcludeCount++ return createInlineErrorText(ref, "Not", "embeddable (syntax="+syntax+")") } |
︙ | ︙ | |||
567 568 569 570 571 572 573 | func (e *evaluator) evaluateEmbeddedInline(content []byte, syntax string) ast.InlineSlice { is := parser.ParseInlines(input.NewInput(content), syntax) ast.Walk(e, &is) return is } func (e *evaluator) evaluateEmbeddedZettel(zettel zettel.Zettel) *ast.ZettelNode { | | | 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | func (e *evaluator) evaluateEmbeddedInline(content []byte, syntax string) ast.InlineSlice { is := parser.ParseInlines(input.NewInput(content), syntax) ast.Walk(e, &is) return is } func (e *evaluator) evaluateEmbeddedZettel(zettel zettel.Zettel) *ast.ZettelNode { zn := parser.ParseZettel(e.ctx, zettel, zettel.Meta.GetDefault(api.KeySyntax, ""), e.rtConfig) ast.Walk(e, &zn.Ast) return zn } func findInlineSlice(bs *ast.BlockSlice, fragment string) ast.InlineSlice { if fragment == "" { return firstInlinesToEmbed(*bs) |
︙ | ︙ |
Changes to evaluator/list.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package evaluator import ( "bytes" "context" |
︙ | ︙ | |||
29 30 31 32 33 34 35 | "zettelstore.de/z/encoding/rss" "zettelstore.de/z/parser" "zettelstore.de/z/query" "zettelstore.de/z/zettel/meta" ) // QueryAction transforms a list of metadata according to query actions into a AST nested list. | | | | | | | | | | | | | < < | < < | | < | | > | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | "zettelstore.de/z/encoding/rss" "zettelstore.de/z/parser" "zettelstore.de/z/query" "zettelstore.de/z/zettel/meta" ) // QueryAction transforms a list of metadata according to query actions into a AST nested list. func QueryAction(ctx context.Context, q *query.Query, ml []*meta.Meta, rtConfig config.Config) ast.BlockNode { ap := actionPara{ ctx: ctx, q: q, ml: ml, kind: ast.NestedListUnordered, min: -1, max: -1, title: rtConfig.GetSiteName(), } actions := q.Actions() if len(actions) == 0 { return ap.createBlockNodeMeta("") } acts := make([]string, 0, len(actions)) for i, act := range actions { if strings.HasPrefix(act, "N") { ap.kind = ast.NestedListOrdered continue } if strings.HasPrefix(act, "MIN") { if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 { ap.min = num continue } } if strings.HasPrefix(act, "MAX") { if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 { ap.max = num continue } } if act == "TITLE" && i+1 < len(actions) { ap.title = strings.Join(actions[i+1:], " ") break } if act == "REINDEX" { continue } acts = append(acts, act) } var firstUnknownKey string for _, act := range acts { switch act { case "ATOM": return ap.createBlockNodeAtom(rtConfig) case "RSS": return ap.createBlockNodeRSS(rtConfig) case "KEYS": return ap.createBlockNodeMetaKeys() } key := strings.ToLower(act) switch meta.Type(key) { case meta.TypeWord: return ap.createBlockNodeWord(key) case meta.TypeTagSet: return ap.createBlockNodeTagSet(key) } if firstUnknownKey == "" { firstUnknownKey = key } } return ap.createBlockNodeMeta(firstUnknownKey) } type actionPara struct { ctx context.Context q *query.Query ml []*meta.Meta kind ast.NestedListKind min int max int title string } func (ap *actionPara) createBlockNodeWord(key string) ast.BlockNode { var buf bytes.Buffer ccs, bufLen := ap.prepareCatAction(key, &buf) if len(ccs) == 0 { return nil } items := make([]ast.ItemSlice, 0, len(ccs)) ccs.SortByName() for _, cat := range ccs { buf.WriteString(cat.Name) items = append(items, ast.ItemSlice{ast.CreateParaNode(&ast.LinkNode{ Attrs: nil, Ref: ast.ParseReference(buf.String()), Inlines: ast.InlineSlice{&ast.TextNode{Text: cat.Name}}, })}) buf.Truncate(bufLen) } return &ast.NestedListNode{ Kind: ap.kind, Items: items, Attrs: nil, } } func (ap *actionPara) createBlockNodeTagSet(key string) ast.BlockNode { var buf bytes.Buffer ccs, bufLen := ap.prepareCatAction(key, &buf) if len(ccs) == 0 { return nil } ccs.SortByCount() ccs = ap.limitTags(ccs) countMap := ap.calcFontSizes(ccs) para := make(ast.InlineSlice, 0, len(ccs)) ccs.SortByName() |
︙ | ︙ | |||
166 167 168 169 170 171 172 | Kind: ast.FormatSuper, Attrs: nil, Inlines: ast.InlineSlice{&ast.TextNode{Text: strconv.Itoa(cat.Count)}}, }, ) buf.Truncate(bufLen) } | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | Kind: ast.FormatSuper, Attrs: nil, Inlines: ast.InlineSlice{&ast.TextNode{Text: strconv.Itoa(cat.Count)}}, }, ) buf.Truncate(bufLen) } return &ast.ParaNode{Inlines: para} } func (ap *actionPara) limitTags(ccs meta.CountedCategories) meta.CountedCategories { if min, max := ap.min, ap.max; min > 0 || max > 0 { if min < 0 { min = ccs[len(ccs)-1].Count } |
︙ | ︙ | |||
190 191 192 193 194 195 196 | } return temp } } return ccs } | | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | } return temp } } return ccs } func (ap *actionPara) createBlockNodeMetaKeys() ast.BlockNode { arr := make(meta.Arrangement, 128) for _, m := range ap.ml { for k := range m.Map() { arr[k] = append(arr[k], m) } } if len(arr) == 0 { return nil } ccs := arr.Counted() ccs.SortByName() var buf bytes.Buffer bufLen := ap.prepareSimpleQuery(&buf) items := make([]ast.ItemSlice, 0, len(ccs)) |
︙ | ︙ | |||
236 237 238 239 240 241 242 | &ast.TextNode{Text: ")"}, )}) } return &ast.NestedListNode{ Kind: ap.kind, Items: items, Attrs: nil, | < | | > | | < > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | &ast.TextNode{Text: ")"}, )}) } return &ast.NestedListNode{ Kind: ap.kind, Items: items, Attrs: nil, } } func (ap *actionPara) createBlockNodeMeta(key string) ast.BlockNode { if len(ap.ml) == 0 { return nil } items := make([]ast.ItemSlice, 0, len(ap.ml)) for _, m := range ap.ml { if key != "" { if _, found := m.Get(key); !found { continue } } items = append(items, ast.ItemSlice{ast.CreateParaNode(&ast.LinkNode{ Attrs: nil, Ref: ast.ParseReference(m.Zid.String()), Inlines: parser.ParseSpacedText(m.GetTitle()), })}) } return &ast.NestedListNode{ Kind: ap.kind, Items: items, Attrs: nil, } } func (ap *actionPara) prepareCatAction(key string, buf *bytes.Buffer) (meta.CountedCategories, int) { if len(ap.ml) == 0 { return nil, 0 } ccs := meta.CreateArrangement(ap.ml, key).Counted() |
︙ | ︙ | |||
297 298 299 300 301 302 303 | const fontSizes = 6 // Must be the number of CSS classes zs-font-size-* in base.css const fontSizes64 = float64(fontSizes) func (*actionPara) calcFontSizes(ccs meta.CountedCategories) map[int]attrs.Attributes { var fsAttrs [fontSizes]attrs.Attributes var a attrs.Attributes | | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | const fontSizes = 6 // Must be the number of CSS classes zs-font-size-* in base.css const fontSizes64 = float64(fontSizes) func (*actionPara) calcFontSizes(ccs meta.CountedCategories) map[int]attrs.Attributes { var fsAttrs [fontSizes]attrs.Attributes var a attrs.Attributes for i := 0; i < fontSizes; i++ { fsAttrs[i] = a.AddClass("zs-font-size-" + strconv.Itoa(i)) } countMap := make(map[int]int, len(ccs)) for _, cat := range ccs { countMap[cat.Count]++ } |
︙ | ︙ | |||
347 348 349 350 351 352 353 | } } return result } func calcBudget(total, curSize float64) float64 { return math.Round(total / (fontSizes64 - curSize)) } | | < | | > | < | > | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | } } return result } func calcBudget(total, curSize float64) float64 { return math.Round(total / (fontSizes64 - curSize)) } func (ap *actionPara) createBlockNodeRSS(cfg config.Config) ast.BlockNode { var rssConfig rss.Configuration rssConfig.Setup(ap.ctx, cfg) rssConfig.Title = ap.title data := rssConfig.Marshal(ap.q, ap.ml) return &ast.VerbatimNode{ Kind: ast.VerbatimProg, Attrs: attrs.Attributes{"lang": "xml"}, Content: data, } } func (ap *actionPara) createBlockNodeAtom(cfg config.Config) ast.BlockNode { var atomConfig atom.Configuration atomConfig.Setup(cfg) atomConfig.Title = ap.title data := atomConfig.Marshal(ap.q, ap.ml) return &ast.VerbatimNode{ Kind: ast.VerbatimProg, Attrs: attrs.Attributes{"lang": "xml"}, Content: data, } } |
Changes to evaluator/metadata.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package evaluator import ( "zettelstore.de/z/ast" "zettelstore.de/z/zettel/meta" |
︙ | ︙ |
Changes to go.mod.
1 2 | module zettelstore.de/z | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | module zettelstore.de/z go 1.21 require ( github.com/fsnotify/fsnotify v1.7.0 github.com/yuin/goldmark v1.6.0 golang.org/x/crypto v0.16.0 golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 zettelstore.de/client.fossil v0.0.0-20231130151508-751754d40c73 zettelstore.de/sx.fossil v0.0.0-20231130150648-05ef116ba207 ) require golang.org/x/sys v0.15.0 // indirect |
Changes to go.sum.
1 2 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= zettelstore.de/client.fossil v0.0.0-20231130151508-751754d40c73 h1:LPF8QWip3GRLBzgPdnsJnrvk1sGiMOSpqOwURiax9gg= zettelstore.de/client.fossil v0.0.0-20231130151508-751754d40c73/go.mod h1:fN+1WxRorSbHduS0T0B4GI8o82EgFuUWBv6fwsAZF6o= zettelstore.de/sx.fossil v0.0.0-20231130150648-05ef116ba207 h1:8ch54z0w53bps6a00NDofEqo3AJ1l7ITXyC3XyLmlY4= zettelstore.de/sx.fossil v0.0.0-20231130150648-05ef116ba207/go.mod h1:Uw3OLM1ufOM4Xe0G51mvkTDUv2okd+HyDBMx+0ZG7ME= |
Added input/entity.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package input import ( "html" "unicode" ) // ScanEntity scans either a named or a numbered entity and returns it as a string. // // For numbered entities (like { or ģ) html.UnescapeString returns // sometimes other values as expected, if the number is not well-formed. This // may happen because of some strange HTML parsing rules. But these do not // apply to Zettelmarkup. Therefore, I parse the number here in the code. func (inp *Input) ScanEntity() (res string, success bool) { if inp.Ch != '&' { return "", false } pos := inp.Pos inp.Next() if inp.Ch == '#' { inp.Next() if inp.Ch == 'x' || inp.Ch == 'X' { return inp.scanEntityBase16() } return inp.scanEntityBase10() } return inp.scanEntityNamed(pos) } func (inp *Input) scanEntityBase16() (string, bool) { inp.Next() if inp.Ch == ';' { return "", false } code := 0 for { switch ch := inp.Ch; ch { case ';': inp.Next() if r := rune(code); isValidEntity(r) { return string(r), true } return "", false case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': code = 16*code + int(ch-'0') case 'a', 'b', 'c', 'd', 'e', 'f': code = 16*code + int(ch-'a'+10) case 'A', 'B', 'C', 'D', 'E', 'F': code = 16*code + int(ch-'A'+10) default: return "", false } if code > unicode.MaxRune { return "", false } inp.Next() } } func (inp *Input) scanEntityBase10() (string, bool) { // Base 10 code if inp.Ch == ';' { return "", false } code := 0 for { switch ch := inp.Ch; ch { case ';': inp.Next() if r := rune(code); isValidEntity(r) { return string(r), true } return "", false case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': code = 10*code + int(ch-'0') default: return "", false } if code > unicode.MaxRune { return "", false } inp.Next() } } func (inp *Input) scanEntityNamed(pos int) (string, bool) { for { switch inp.Ch { case EOS, '\n', '\r', '&': return "", false case ';': inp.Next() es := string(inp.Src[pos:inp.Pos]) ues := html.UnescapeString(es) if es == ues { return "", false } return ues, true default: inp.Next() } } } // isValidEntity checks if the given code is valid for an entity. // // According to https://html.spec.whatwg.org/multipage/syntax.html#character-references // ""The numeric character reference forms described above are allowed to reference any code point // excluding U+000D CR, noncharacters, and controls other than ASCII whitespace."" func isValidEntity(r rune) bool { // No C0 control and no "code point in the range U+007F DELETE to U+009F APPLICATION PROGRAM COMMAND, inclusive." if r < ' ' || ('\u007f' <= r && r <= '\u009f') { return false } // If below any noncharacter code point, return true // // See: https://infra.spec.whatwg.org/#noncharacter if r < '\ufdd0' { return true } // First range of noncharacter code points: "(...) in the range U+FDD0 to U+FDEF, inclusive" if r <= '\ufdef' { return false } // Other noncharacter code points: switch r { case '\uFFFE', '\uFFFF', '\U0001FFFE', '\U0001FFFF', '\U0002FFFE', '\U0002FFFF', '\U0003FFFE', '\U0003FFFF', '\U0004FFFE', '\U0004FFFF', '\U0005FFFE', '\U0005FFFF', '\U0006FFFE', '\U0006FFFF', '\U0007FFFE', '\U0007FFFF', '\U0008FFFE', '\U0008FFFF', '\U0009FFFE', '\U0009FFFF', '\U000AFFFE', '\U000AFFFF', '\U000BFFFE', '\U000BFFFF', '\U000CFFFE', '\U000CFFFF', '\U000DFFFE', '\U000DFFFF', '\U000EFFFE', '\U000EFFFF', '\U000FFFFE', '\U000FFFFF', '\U0010FFFE', '\U0010FFFF': return false } return true } |
Added input/entity_test.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package input_test import ( "testing" "zettelstore.de/z/input" ) func TestScanEntity(t *testing.T) { t.Parallel() var testcases = []struct { text string exp string }{ {"", ""}, {"a", ""}, {"&", "&"}, {"!", "!"}, {"3", "3"}, {""", "\""}, } for id, tc := range testcases { inp := input.NewInput([]byte(tc.text)) got, ok := inp.ScanEntity() if !ok { if tc.exp != "" { t.Errorf("ID=%d, text=%q: expected error, but got %q", id, tc.text, got) } if inp.Pos != 0 { t.Errorf("ID=%d, text=%q: input position advances to %d", id, tc.text, inp.Pos) } continue } if tc.exp != got { t.Errorf("ID=%d, text=%q: expected %q, but got %q", id, tc.text, tc.exp, got) } } } func TestScanIllegalEntity(t *testing.T) { t.Parallel() testcases := []string{"", "a", "& Input →", "	", ""} for i, tc := range testcases { inp := input.NewInput([]byte(tc)) got, ok := inp.ScanEntity() if ok { t.Errorf("%d: scanning %q was unexpected successful, got %q", i, tc, got) continue } } } |
Added input/input.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package input provides an abstraction for data to be read. package input import "unicode/utf8" // Input is an abstract input source type Input struct { // Read-only, will never change Src []byte // The source string // Read-only, will change Ch rune // current character Pos int // character position in src readPos int // reading position (position after current character) } // NewInput creates a new input source. func NewInput(src []byte) *Input { inp := &Input{Src: src} inp.Next() return inp } // EOS = End of source const EOS = rune(-1) // Next reads the next rune into inp.Ch and returns it too. func (inp *Input) Next() rune { if inp.readPos >= len(inp.Src) { inp.Pos = len(inp.Src) inp.Ch = EOS return EOS } inp.Pos = inp.readPos r, w := rune(inp.Src[inp.readPos]), 1 if r >= utf8.RuneSelf { r, w = utf8.DecodeRune(inp.Src[inp.readPos:]) } inp.readPos += w inp.Ch = r return r } // Peek returns the rune following the most recently read rune without // advancing. If end-of-source was already found peek returns EOS. func (inp *Input) Peek() rune { return inp.PeekN(0) } // PeekN returns the n-th rune after the most recently read rune without // advancing. If end-of-source was already found peek returns EOS. func (inp *Input) PeekN(n int) rune { pos := inp.readPos + n if pos < len(inp.Src) { r := rune(inp.Src[pos]) if r >= utf8.RuneSelf { r, _ = utf8.DecodeRune(inp.Src[pos:]) } if r == '\t' { return ' ' } return r } return EOS } // Accept checks if the given string is a prefix of the text to be parsed. // If successful, advance position and current character. // String must only contain bytes < 128. // If not successful, everything remains as it is. func (inp *Input) Accept(s string) bool { pos := inp.Pos remaining := len(inp.Src) - pos if s == "" || len(s) > remaining { return false } // According to internal documentation of bytes.Equal, the string() will not allocate any memory. if readPos := pos + len(s); s == string(inp.Src[pos:readPos]) { inp.readPos = readPos inp.Next() return true } return false } // IsEOLEOS returns true if char is either EOS or EOL. func IsEOLEOS(ch rune) bool { switch ch { case EOS, '\n', '\r': return true } return false } // EatEOL transforms both "\r" and "\r\n" into "\n". func (inp *Input) EatEOL() { switch inp.Ch { case '\r': if inp.Peek() == '\n' { inp.Next() } inp.Ch = '\n' inp.Next() case '\n': inp.Next() } } // SetPos allows to reset the read position. func (inp *Input) SetPos(pos int) { if inp.Pos != pos { inp.readPos = pos inp.Next() } } // SkipToEOL reads until the next end-of-line. func (inp *Input) SkipToEOL() { for { switch inp.Ch { case EOS, '\n', '\r': return } inp.Next() } } // ScanLineContent reads the reaining input stream and interprets it as lines of text. func (inp *Input) ScanLineContent() []byte { result := make([]byte, 0, len(inp.Src)-inp.Pos+1) for { inp.EatEOL() posL := inp.Pos if inp.Ch == EOS { return result } inp.SkipToEOL() if len(result) > 0 { result = append(result, '\n') } result = append(result, inp.Src[posL:inp.Pos]...) } } |
Added input/input_test.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package input_test provides some unit-tests for reading data. package input_test import ( "testing" "zettelstore.de/z/input" ) func TestEatEOL(t *testing.T) { t.Parallel() inp := input.NewInput(nil) inp.EatEOL() if inp.Ch != input.EOS { t.Errorf("No EOS found: %q", inp.Ch) } if inp.Pos != 0 { t.Errorf("Pos != 0: %d", inp.Pos) } inp = input.NewInput([]byte("ABC")) if inp.Ch != 'A' { t.Errorf("First ch != 'A', got %q", inp.Ch) } inp.EatEOL() if inp.Ch != 'A' { t.Errorf("First ch != 'A', got %q", inp.Ch) } } func TestAccept(t *testing.T) { t.Parallel() testcases := []struct { accept string src string acc bool exp rune }{ {"", "", false, input.EOS}, {"AB", "abc", false, 'a'}, {"AB", "ABC", true, 'C'}, {"AB", "AB", true, input.EOS}, {"AB", "A", false, 'A'}, } for i, tc := range testcases { inp := input.NewInput([]byte(tc.src)) acc := inp.Accept(tc.accept) if acc != tc.acc { t.Errorf("%d: %q.Accept(%q) == %v, but got %v", i, tc.src, tc.accept, tc.acc, acc) } if got := inp.Ch; tc.exp != got { t.Errorf("%d: %q.Accept(%q) should result in run %v, but got %v", i, tc.src, tc.accept, tc.exp, got) } } } |
Added input/runes.go.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package input import "unicode" // IsSpace returns true if rune is a whitespace. func IsSpace(ch rune) bool { switch ch { case ' ', '\t': return true case '\n', '\r', EOS: return false } return unicode.IsSpace(ch) } |
Changes to kernel/impl/auth.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "errors" "sync" |
︙ | ︙ | |||
71 72 73 74 75 76 77 | func (as *authService) Start(*myKernel) error { as.mxService.Lock() defer as.mxService.Unlock() readonlyMode := as.GetNextConfig(kernel.AuthReadonly).(bool) owner := as.GetNextConfig(kernel.AuthOwner).(id.Zid) authMgr, err := as.createManager(readonlyMode, owner) if err != nil { | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | func (as *authService) Start(*myKernel) error { as.mxService.Lock() defer as.mxService.Unlock() readonlyMode := as.GetNextConfig(kernel.AuthReadonly).(bool) owner := as.GetNextConfig(kernel.AuthOwner).(id.Zid) authMgr, err := as.createManager(readonlyMode, owner) if err != nil { as.logger.Fatal().Err(err).Msg("Unable to create manager") return err } as.logger.Info().Msg("Start Manager") as.manager = authMgr return nil } |
︙ | ︙ |
Changes to kernel/impl/box.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "context" "errors" |
︙ | ︙ | |||
81 82 83 84 85 86 87 | } boxURIs = append(boxURIs, u.(*url.URL)) } ps.mxService.Lock() defer ps.mxService.Unlock() mgr, err := ps.createManager(boxURIs, kern.auth.manager, &kern.cfg) if err != nil { | | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | } boxURIs = append(boxURIs, u.(*url.URL)) } ps.mxService.Lock() defer ps.mxService.Unlock() mgr, err := ps.createManager(boxURIs, kern.auth.manager, &kern.cfg) if err != nil { ps.logger.Fatal().Err(err).Msg("Unable to create manager") return err } ps.logger.Info().Str("location", mgr.Location()).Msg("Start Manager") if err = mgr.Start(context.Background()); err != nil { ps.logger.Fatal().Err(err).Msg("Unable to start manager") return err } kern.cfg.setBox(mgr) ps.manager = mgr return nil } |
︙ | ︙ |
Changes to kernel/impl/cfg.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "context" "errors" |
︙ | ︙ |
Changes to kernel/impl/cmd.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "fmt" "io" |
︙ | ︙ |
Changes to kernel/impl/config.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "errors" "fmt" |
︙ | ︙ |
Changes to kernel/impl/core.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "fmt" "net" |
︙ | ︙ |
Changes to kernel/impl/impl.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package impl provides the kernel implementation. package impl import ( "errors" |
︙ | ︙ | |||
70 71 72 73 74 75 76 | srv service srvnum kernel.Service } type serviceDependency map[kernel.Service][]kernel.Service const ( defaultNormalLogLevel = logger.InfoLevel | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | srv service srvnum kernel.Service } type serviceDependency map[kernel.Service][]kernel.Service const ( defaultNormalLogLevel = logger.InfoLevel defaultSimpleLogLevel = logger.WarnLevel ) // create a new kernel. func init() { kernel.Main = createKernel() } |
︙ | ︙ | |||
98 99 100 101 102 103 104 | kernel.AuthService: {&kern.auth, "auth", defaultNormalLogLevel}, kernel.BoxService: {&kern.box, "box", defaultNormalLogLevel}, kernel.WebService: {&kern.web, "web", defaultNormalLogLevel}, } kern.srvNames = make(map[string]serviceData, len(kern.srvs)) for key, srvD := range kern.srvs { if _, ok := kern.srvNames[srvD.name]; ok { | | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | kernel.AuthService: {&kern.auth, "auth", defaultNormalLogLevel}, kernel.BoxService: {&kern.box, "box", defaultNormalLogLevel}, kernel.WebService: {&kern.web, "web", defaultNormalLogLevel}, } kern.srvNames = make(map[string]serviceData, len(kern.srvs)) for key, srvD := range kern.srvs { if _, ok := kern.srvNames[srvD.name]; ok { kern.logger.Panic().Str("service", srvD.name).Msg("Service data already set") } kern.srvNames[srvD.name] = serviceData{srvD.srv, key} l := logger.New(lw, strings.ToUpper(srvD.name)).SetLevel(srvD.logLevel) kern.logger.Debug().Str("service", srvD.name).Msg("Initialize") srvD.srv.Initialize(l) } kern.depStart = serviceDependency{ |
︙ | ︙ | |||
165 166 167 168 169 170 171 | logger.Mandatory().Msg("Licensed under the latest version of the EUPL (European Union Public License)") if configFilename != "" { logger.Mandatory().Str("filename", configFilename).Msg("Configuration file found") } else { logger.Mandatory().Msg("No configuration file found / used") } if kern.core.GetCurConfig(kernel.CoreDebug).(bool) { | | | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | logger.Mandatory().Msg("Licensed under the latest version of the EUPL (European Union Public License)") if configFilename != "" { logger.Mandatory().Str("filename", configFilename).Msg("Configuration file found") } else { logger.Mandatory().Msg("No configuration file found / used") } if kern.core.GetCurConfig(kernel.CoreDebug).(bool) { logger.Warn().Msg("----------------------------------------") logger.Warn().Msg("DEBUG MODE, DO NO USE THIS IN PRODUCTION") logger.Warn().Msg("----------------------------------------") } if kern.auth.GetCurConfig(kernel.AuthReadonly).(bool) { logger.Info().Msg("Read-only mode") } } if lineServer { port := kern.core.GetNextConfig(kernel.CorePort).(int) |
︙ | ︙ | |||
277 278 279 280 281 282 283 | // LogRecover outputs some information about the previous panic. func (kern *myKernel) LogRecover(name string, recoverInfo interface{}) bool { return kern.doLogRecover(name, recoverInfo) } func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool { stack := debug.Stack() | | | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | // LogRecover outputs some information about the previous panic. func (kern *myKernel) LogRecover(name string, recoverInfo interface{}) bool { return kern.doLogRecover(name, recoverInfo) } func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool { stack := debug.Stack() kern.logger.Fatal().Str("recovered_from", fmt.Sprint(recoverInfo)).Bytes("stack", stack).Msg(name) kern.core.updateRecoverInfo(name, recoverInfo, stack) return true } // --- Profiling --------------------------------------------------------- var errProfileInWork = errors.New("already profiling") |
︙ | ︙ |
Changes to kernel/impl/log.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "os" "sync" |
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 | } buf = append(buf, msg...) buf = append(buf, details...) buf = append(buf, '\n') _, err := os.Stdout.Write(buf) klw.mx.Unlock() return err } func addTimestamp(buf *[]byte, ts time.Time) { year, month, day := ts.Date() itoa(buf, year, 4) *buf = append(*buf, '-') | > > > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | } buf = append(buf, msg...) buf = append(buf, details...) buf = append(buf, '\n') _, err := os.Stdout.Write(buf) klw.mx.Unlock() if level == logger.PanicLevel { panic(err) } return err } func addTimestamp(buf *[]byte, ts time.Time) { year, month, day := ts.Date() itoa(buf, year, 4) *buf = append(*buf, '-') |
︙ | ︙ | |||
122 123 124 125 126 127 128 | defer klw.mx.RUnlock() if !klw.full { if klw.writePos == 0 { return nil } result := make([]kernel.LogEntry, klw.writePos) | | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | defer klw.mx.RUnlock() if !klw.full { if klw.writePos == 0 { return nil } result := make([]kernel.LogEntry, klw.writePos) for i := 0; i < klw.writePos; i++ { copyE2E(&result[i], &klw.data[i]) } return result } result := make([]kernel.LogEntry, cap(klw.data)) pos := 0 for j := klw.writePos; j < cap(klw.data); j++ { copyE2E(&result[pos], &klw.data[j]) pos++ } for j := 0; j < klw.writePos; j++ { copyE2E(&result[pos], &klw.data[j]) pos++ } return result } func (klw *kernelLogWriter) getLastLogTime() time.Time { |
︙ | ︙ |
Changes to kernel/impl/server.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "bufio" "net" ) func startLineServer(kern *myKernel, listenAddr string) error { ln, err := net.Listen("tcp", listenAddr) if err != nil { kern.logger.Fatal().Err(err).Msg("Unable to start administration console") return err } kern.logger.Mandatory().Str("listen", listenAddr).Msg("Start administration console") go func() { lineServer(ln, kern) }() return nil } |
︙ | ︙ |
Changes to kernel/impl/web.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "errors" "net" |
︙ | ︙ | |||
145 146 147 148 149 150 151 | secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool) maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64) if maxRequestSize < 1024 { maxRequestSize = 1024 } if !strings.HasSuffix(baseURL, urlPrefix) { | | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool) maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64) if maxRequestSize < 1024 { maxRequestSize = 1024 } if !strings.HasSuffix(baseURL, urlPrefix) { ws.logger.Fatal().Str("base-url", baseURL).Str("url-prefix", urlPrefix).Msg( "url-prefix is not a suffix of base-url") return errWrongBasePrefix } if lap := netip.MustParseAddrPort(listenAddr); !kern.auth.manager.WithAuth() && !lap.Addr().IsLoopback() { ws.logger.Warn().Str("listen", listenAddr).Msg("service may be reached from outside, but authentication is not enabled") } srvw := impl.New(ws.logger, listenAddr, baseURL, urlPrefix, persistentCookie, secureCookie, maxRequestSize, kern.auth.manager) err := kern.web.setupServer(srvw, kern.box.manager, kern.auth.manager, &kern.cfg) if err != nil { ws.logger.Fatal().Err(err).Msg("Unable to create") return err } if kern.core.GetNextConfig(kernel.CoreDebug).(bool) { srvw.SetDebug() } if err = srvw.Run(); err != nil { ws.logger.Fatal().Err(err).Msg("Unable to start") return err } ws.logger.Info().Str("listen", listenAddr).Str("base-url", baseURL).Msg("Start Service") ws.mxService.Lock() ws.srvw = srvw ws.mxService.Unlock() |
︙ | ︙ |
Changes to kernel/kernel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package kernel provides the main kernel service. package kernel import ( "io" |
︙ | ︙ |
Changes to logger/logger.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package logger implements a logging package for use in the Zettelstore. package logger import ( "context" |
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | type Level uint8 // Constants for Level const ( NoLevel Level = iota // the absent log level TraceLevel // Log most internal activities DebugLevel // Log most data updates InfoLevel // Log normal activities ErrorLevel // Log (persistent) errors MandatoryLevel // Log only mandatory events NeverLevel // Logging is disabled ) var logLevel = [...]string{ " ", "TRACE", "DEBUG", "INFO ", "ERROR", ">>>>>", "NEVER", } var strLevel = [...]string{ "", "trace", "debug", "info", "error", "mandatory", "disabled", } // IsValid returns true, if the level is a valid level func (l Level) IsValid() bool { return TraceLevel <= l && l <= NeverLevel } | > > > > > > > > > > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | type Level uint8 // Constants for Level const ( NoLevel Level = iota // the absent log level TraceLevel // Log most internal activities DebugLevel // Log most data updates SenseLevel // Log activities of minor interest InfoLevel // Log normal activities WarnLevel // Log event that can be easily recovered ErrorLevel // Log (persistent) errors FatalLevel // Log event that cannot be recovered within an internal acitivty PanicLevel // Log event that must stop the software MandatoryLevel // Log only mandatory events NeverLevel // Logging is disabled ) var logLevel = [...]string{ " ", "TRACE", "DEBUG", "SENSE", "INFO ", "WARN ", "ERROR", "FATAL", "PANIC", ">>>>>", "NEVER", } var strLevel = [...]string{ "", "trace", "debug", "sense", "info", "warn", "error", "fatal", "panic", "mandatory", "disabled", } // IsValid returns true, if the level is a valid level func (l Level) IsValid() bool { return TraceLevel <= l && l <= NeverLevel } |
︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | // Trace creates a tracing message. func (l *Logger) Trace() *Message { return newMessage(l, TraceLevel) } // Debug creates a debug message. func (l *Logger) Debug() *Message { return newMessage(l, DebugLevel) } // Info creates a message suitable for information data. func (l *Logger) Info() *Message { return newMessage(l, InfoLevel) } // Error creates a message suitable for errors. func (l *Logger) Error() *Message { return newMessage(l, ErrorLevel) } // Mandatory creates a message that will always logged, except when logging // is disabled. func (l *Logger) Mandatory() *Message { return newMessage(l, MandatoryLevel) } // Clone creates a message to clone the logger. func (l *Logger) Clone() *Message { msg := newMessage(l, NeverLevel) | > > > > > > > > > > > > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | // Trace creates a tracing message. func (l *Logger) Trace() *Message { return newMessage(l, TraceLevel) } // Debug creates a debug message. func (l *Logger) Debug() *Message { return newMessage(l, DebugLevel) } // Sense creates a message suitable for sensing data. func (l *Logger) Sense() *Message { return newMessage(l, SenseLevel) } // Info creates a message suitable for information data. func (l *Logger) Info() *Message { return newMessage(l, InfoLevel) } // Warn creates a message suitable for warning the user. func (l *Logger) Warn() *Message { return newMessage(l, WarnLevel) } // Error creates a message suitable for errors. func (l *Logger) Error() *Message { return newMessage(l, ErrorLevel) } // Fatal creates a message suitable for fatal errors. func (l *Logger) Fatal() *Message { return newMessage(l, FatalLevel) } // Panic creates a message suitable for panicing. func (l *Logger) Panic() *Message { return newMessage(l, PanicLevel) } // Mandatory creates a message that will always logged, except when logging // is disabled. func (l *Logger) Mandatory() *Message { return newMessage(l, MandatoryLevel) } // Clone creates a message to clone the logger. func (l *Logger) Clone() *Message { msg := newMessage(l, NeverLevel) |
︙ | ︙ |
Changes to logger/logger_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package logger_test import ( "fmt" "os" |
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | testcases := []struct { text string exp logger.Level }{ {"tra", logger.TraceLevel}, {"deb", logger.DebugLevel}, {"info", logger.InfoLevel}, {"err", logger.ErrorLevel}, {"manda", logger.MandatoryLevel}, {"dis", logger.NeverLevel}, {"d", logger.Level(0)}, } for i, tc := range testcases { got := logger.ParseLevel(tc.text) if got != tc.exp { t.Errorf("%d: ParseLevel(%q) == %q, but got %q", i, tc.text, tc.exp, got) } } } func BenchmarkDisabled(b *testing.B) { log := logger.New(&stderrLogWriter{}, "").SetLevel(logger.NeverLevel) | > > > | | | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | testcases := []struct { text string exp logger.Level }{ {"tra", logger.TraceLevel}, {"deb", logger.DebugLevel}, {"info", logger.InfoLevel}, {"warn", logger.WarnLevel}, {"err", logger.ErrorLevel}, {"fata", logger.FatalLevel}, {"pan", logger.PanicLevel}, {"manda", logger.MandatoryLevel}, {"dis", logger.NeverLevel}, {"d", logger.Level(0)}, } for i, tc := range testcases { got := logger.ParseLevel(tc.text) if got != tc.exp { t.Errorf("%d: ParseLevel(%q) == %q, but got %q", i, tc.text, tc.exp, got) } } } func BenchmarkDisabled(b *testing.B) { log := logger.New(&stderrLogWriter{}, "").SetLevel(logger.NeverLevel) for n := 0; n < b.N; n++ { log.Info().Str("key", "val").Msg("Benchmark") } } type stderrLogWriter struct{} func (*stderrLogWriter) WriteMessage(level logger.Level, ts time.Time, prefix, msg string, details []byte) error { fmt.Fprintf(os.Stderr, "%v %v %v %v %v\n", level.Format(), ts, prefix, msg, string(details)) return nil } type testLogWriter struct{} func (*testLogWriter) WriteMessage(logger.Level, time.Time, string, string, []byte) error { return nil } func BenchmarkStrMessage(b *testing.B) { log := logger.New(&testLogWriter{}, "") for n := 0; n < b.N; n++ { log.Info().Str("key", "val").Msg("Benchmark") } } func BenchmarkMessage(b *testing.B) { log := logger.New(&testLogWriter{}, "") for n := 0; n < b.N; n++ { log.Info().Msg("Benchmark") } } func BenchmarkCloneStrMessage(b *testing.B) { log := logger.New(&testLogWriter{}, "").Clone().Str("sss", "ttt").Child() for n := 0; n < b.N; n++ { log.Info().Msg("123456789") } } |
Changes to logger/message.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package logger import ( "context" "net/http" |
︙ | ︙ |
Changes to parser/blob/blob.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package blob provides a parser of binary data. package blob import ( "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxGif, |
︙ | ︙ |
Changes to parser/cleaner/cleaner.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package cleaner provides functions to clean up the parsed AST. package cleaner import ( "strconv" |
︙ | ︙ |
Changes to parser/draw/canvas.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" |
︙ | ︙ | |||
96 97 98 99 100 101 102 | c.findTexts() sort.Sort(c.objs) } // findPaths by starting with a point that wasn't yet visited, beginning at the top // left of the grid. func (c *canvas) findPaths() { | | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | c.findTexts() sort.Sort(c.objs) } // findPaths by starting with a point that wasn't yet visited, beginning at the top // left of the grid. func (c *canvas) findPaths() { for y := 0; y < c.siz.Y; y++ { p := point{y: y} for x := 0; x < c.siz.X; x++ { p.x = x if c.isVisited(p) { continue } ch := c.at(p) if !ch.isPathStart() { continue |
︙ | ︙ | |||
126 127 128 129 130 131 132 | c.objs = append(c.objs, objs...) } } } // findTexts with a second pass through the grid attempts to identify any text within the grid. func (c *canvas) findTexts() { | | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | c.objs = append(c.objs, objs...) } } } // findTexts with a second pass through the grid attempts to identify any text within the grid. func (c *canvas) findTexts() { for y := 0; y < c.siz.Y; y++ { p := point{} p.y = y for x := 0; x < c.siz.X; x++ { p.x = x if c.isVisited(p) { continue } ch := c.at(p) if !ch.isTextStart() { continue |
︙ | ︙ |
Changes to parser/draw/canvas_test.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "reflect" "strings" |
︙ | ︙ | |||
657 658 659 660 661 662 663 | " | | | | |", " +-----+-------+---------+---+", "", "", } chunk := []byte(strings.Join(data, "\n")) input := make([]byte, 0, len(chunk)*b.N) | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | " | | | | |", " +-----+-------+---------+---+", "", "", } chunk := []byte(strings.Join(data, "\n")) input := make([]byte, 0, len(chunk)*b.N) for i := 0; i < b.N; i++ { input = append(input, chunk...) } expected := 30 * b.N b.ResetTimer() c, err := newCanvas(input) if err != nil { b.Fatalf("Error creating canvas: %s", err) |
︙ | ︙ |
Changes to parser/draw/char.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "unicode" type char rune |
︙ | ︙ |
Changes to parser/draw/draw.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package draw provides a parser to create SVG from ASCII drawing. // // It is not a parser registered by the general parser framework (directed by // metadata "syntax" of a zettel). It will be used when a zettel is evaluated. package draw import ( "strconv" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxDraw, |
︙ | ︙ |
Changes to parser/draw/draw_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package draw_test import ( "testing" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func FuzzParseBlocks(f *testing.F) { f.Fuzz(func(t *testing.T, src []byte) { t.Parallel() |
︙ | ︙ |
Changes to parser/draw/object.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "fmt" // object represents one of an open path, a closed path, or text. |
︙ | ︙ |
Changes to parser/draw/point.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "fmt" // A renderHint suggests ways the SVG renderer may appropriately represent this point. |
︙ | ︙ |
Changes to parser/draw/svg.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" |
︙ | ︙ |
Changes to parser/draw/svg_test.go.
︙ | ︙ | |||
11 12 13 14 15 16 17 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. | < < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "strings" "testing" |
︙ | ︙ |
Changes to parser/markdown/markdown.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package markdown provides a parser for markdown. package markdown import ( "bytes" "fmt" "strconv" "strings" gm "github.com/yuin/goldmark" gmAst "github.com/yuin/goldmark/ast" gmText "github.com/yuin/goldmark/text" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/encoder/textenc" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxMarkdown, |
︙ | ︙ | |||
148 149 150 151 152 153 154 | Content: p.acceptRawText(node), } } func (p *mdP) acceptRawText(node gmAst.Node) []byte { lines := node.Lines() result := make([]byte, 0, 512) | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | Content: p.acceptRawText(node), } } func (p *mdP) acceptRawText(node gmAst.Node) []byte { lines := node.Lines() result := make([]byte, 0, 512) for i := 0; i < lines.Len(); i++ { s := lines.At(i) line := s.Value(p.source) if l := len(line); l > 0 { if l > 1 && line[l-2] == '\r' && line[l-1] == '\n' { line = line[0 : l-2] } else if line[l-1] == '\n' || line[l-1] == '\r' { line = line[0 : l-1] |
︙ | ︙ | |||
464 465 466 467 468 469 470 | Attrs: nil, // TODO }, } } func (p *mdP) acceptRawHTML(node *gmAst.RawHTML) ast.InlineSlice { segs := make([][]byte, 0, node.Segments.Len()) | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | Attrs: nil, // TODO }, } } func (p *mdP) acceptRawHTML(node *gmAst.RawHTML) ast.InlineSlice { segs := make([][]byte, 0, node.Segments.Len()) for i := 0; i < node.Segments.Len(); i++ { segment := node.Segments.At(i) segs = append(segs, segment.Value(p.source)) } return ast.InlineSlice{ &ast.LiteralNode{ Kind: ast.LiteralHTML, Attrs: nil, // TODO: add HTML as language Content: bytes.Join(segs, nil), }, } } |
Changes to parser/markdown/markdown_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package markdown import ( "strings" "testing" |
︙ | ︙ |
Changes to parser/none/none.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package none provides a none-parser, e.g. for zettel with just metadata. package none import ( "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxNone, |
︙ | ︙ |
Changes to parser/parser.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package parser provides a generic interface to a range of different parsers. package parser import ( "context" "fmt" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser/cleaner" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/meta" ) // Info describes a single parser. // |
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | func IsASTParser(syntax string) bool { pi, ok := registry[syntax] if !ok { return false } return pi.IsASTParser } // IsImageFormat returns whether the given syntax is known to be an image format. func IsImageFormat(syntax string) bool { pi, ok := registry[syntax] if !ok { return false } return pi.IsImageFormat } // ParseBlocks parses some input and returns a slice of block nodes. func ParseBlocks(inp *input.Input, m *meta.Meta, syntax string, hi config.HTMLInsecurity) ast.BlockSlice { bs := Get(syntax).ParseBlocks(inp, m, syntax) cleaner.CleanBlockSlice(&bs, hi.AllowHTML(syntax)) return bs } // ParseInlines parses some input and returns a slice of inline nodes. func ParseInlines(inp *input.Input, syntax string) ast.InlineSlice { | > > > > > > > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | func IsASTParser(syntax string) bool { pi, ok := registry[syntax] if !ok { return false } return pi.IsASTParser } // IsTextFormat returns whether the given syntax is known to be a text format. func IsTextFormat(syntax string) bool { pi, ok := registry[syntax] if !ok { return false } return pi.IsTextFormat } // IsImageFormat returns whether the given syntax is known to be an image format. func IsImageFormat(syntax string) bool { pi, ok := registry[syntax] if !ok { return false } return pi.IsImageFormat } // ParseBlocks parses some input and returns a slice of block nodes. func ParseBlocks(inp *input.Input, m *meta.Meta, syntax string, hi config.HTMLInsecurity) ast.BlockSlice { return parseBlocksAndClean(inp, m, syntax, hi) } func parseBlocksAndClean(inp *input.Input, m *meta.Meta, syntax string, hi config.HTMLInsecurity) ast.BlockSlice { bs := Get(syntax).ParseBlocks(inp, m, syntax) cleaner.CleanBlockSlice(&bs, hi.AllowHTML(syntax)) return bs } // ParseInlines parses some input and returns a slice of inline nodes. func ParseInlines(inp *input.Input, syntax string) ast.InlineSlice { |
︙ | ︙ | |||
146 147 148 149 150 151 152 | func ParseZettel(ctx context.Context, zettel zettel.Zettel, syntax string, rtConfig config.Config) *ast.ZettelNode { m := zettel.Meta inhMeta := m if rtConfig != nil { inhMeta = rtConfig.AddDefaultValues(ctx, inhMeta) } if syntax == "" { | | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | func ParseZettel(ctx context.Context, zettel zettel.Zettel, syntax string, rtConfig config.Config) *ast.ZettelNode { m := zettel.Meta inhMeta := m if rtConfig != nil { inhMeta = rtConfig.AddDefaultValues(ctx, inhMeta) } if syntax == "" { syntax, _ = inhMeta.Get(api.KeySyntax) } parseMeta := inhMeta if syntax == meta.SyntaxNone { parseMeta = m } hi := config.NoHTML if rtConfig != nil { hi = rtConfig.GetHTMLInsecurity() } return &ast.ZettelNode{ Meta: m, Content: zettel.Content, Zid: m.Zid, InhMeta: inhMeta, Ast: parseBlocksAndClean(input.NewInput(zettel.Content.AsBytes()), parseMeta, syntax, hi), Syntax: syntax, } } |
Changes to parser/parser_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package parser_test import ( "testing" |
︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 | ) func TestParserType(t *testing.T) { syntaxSet := strfun.NewSet(parser.GetSyntaxes()...) testCases := []struct { syntax string ast bool image bool }{ | > | | | | | | | | | | | | | | | | | > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | ) func TestParserType(t *testing.T) { syntaxSet := strfun.NewSet(parser.GetSyntaxes()...) testCases := []struct { syntax string ast bool text bool image bool }{ {meta.SyntaxHTML, false, true, false}, {meta.SyntaxCSS, false, true, false}, {meta.SyntaxDraw, true, true, false}, {meta.SyntaxGif, false, false, true}, {meta.SyntaxJPEG, false, false, true}, {meta.SyntaxJPG, false, false, true}, {meta.SyntaxMarkdown, true, true, false}, {meta.SyntaxMD, true, true, false}, {meta.SyntaxNone, false, false, false}, {meta.SyntaxPlain, false, true, false}, {meta.SyntaxPNG, false, false, true}, {meta.SyntaxSVG, false, true, true}, {meta.SyntaxSxn, false, true, false}, {meta.SyntaxText, false, true, false}, {meta.SyntaxTxt, false, true, false}, {meta.SyntaxWebp, false, false, true}, {meta.SyntaxZmk, true, true, false}, } for _, tc := range testCases { delete(syntaxSet, tc.syntax) if got := parser.IsASTParser(tc.syntax); got != tc.ast { t.Errorf("Syntax %q is AST: %v, but got %v", tc.syntax, tc.ast, got) } if got := parser.IsTextFormat(tc.syntax); got != tc.text { t.Errorf("Syntax %q is text: %v, but got %v", tc.syntax, tc.text, got) } if got := parser.IsImageFormat(tc.syntax); got != tc.image { t.Errorf("Syntax %q is image: %v, but got %v", tc.syntax, tc.image, got) } } for syntax := range syntaxSet { t.Errorf("Forgot to test syntax %q", syntax) } } |
Changes to parser/plain/plain.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package plain provides a parser for plain text data. package plain import ( "bytes" "strings" "zettelstore.de/client.fossil/attrs" "zettelstore.de/sx.fossil/sxbuiltins" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxTxt, |
︙ | ︙ | |||
134 135 136 137 138 139 140 | } // TODO: check proper end </svg> return svgSrc } func parseSxnBlocks(inp *input.Input, _ *meta.Meta, syntax string) ast.BlockSlice { rd := sxreader.MakeReader(bytes.NewReader(inp.Src)) | | > | | | | | | < < | | | > > > > > > > > > > > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | } // TODO: check proper end </svg> return svgSrc } func parseSxnBlocks(inp *input.Input, _ *meta.Meta, syntax string) ast.BlockSlice { rd := sxreader.MakeReader(bytes.NewReader(inp.Src)) objs, err := rd.ReadAll() if err != nil { return ast.BlockSlice{ &ast.VerbatimNode{ Kind: ast.VerbatimProg, Attrs: attrs.Attributes{"": syntax}, Content: inp.ScanLineContent(), }, ast.CreateParaNode(&ast.TextNode{ Text: err.Error(), }), } } result := make(ast.BlockSlice, len(objs)) for i, obj := range objs { var buf bytes.Buffer sxbuiltins.Print(&buf, obj) result[i] = &ast.VerbatimNode{ Kind: ast.VerbatimProg, Attrs: attrs.Attributes{"": syntax}, Content: buf.Bytes(), } } return result } func parseSxnInlines(inp *input.Input, syntax string) ast.InlineSlice { inp.SkipToEOL() return ast.InlineSlice{&ast.LiteralNode{ Kind: ast.LiteralProg, Attrs: attrs.Attributes{"": syntax}, Content: append([]byte(nil), inp.Src[0:inp.Pos]...), }} } |
Changes to parser/zettelmark/block.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettelmark import ( "fmt" "zettelstore.de/z/ast" "zettelstore.de/z/input" ) // parseBlockSlice parses a sequence of blocks. func (cp *zmkP) parseBlockSlice() ast.BlockSlice { inp := cp.inp var lastPara *ast.ParaNode bs := ast.BlockSlice{} |
︙ | ︙ | |||
407 408 409 410 411 412 413 | } } return ln, newLnCount } func (cp *zmkP) cleanupParsedNestedList(newLnCount int) (res ast.BlockNode, success bool) { listDepth := len(cp.lists) | | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | } } return ln, newLnCount } func (cp *zmkP) cleanupParsedNestedList(newLnCount int) (res ast.BlockNode, success bool) { listDepth := len(cp.lists) for i := 0; i < newLnCount; i++ { childPos := listDepth - i - 1 parentPos := childPos - 1 if parentPos < 0 { return cp.lists[0], true } if prevItems := cp.lists[parentPos].Items; len(prevItems) > 0 { lastItem := len(prevItems) - 1 |
︙ | ︙ |
Changes to parser/zettelmark/inline.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettelmark import ( "bytes" "fmt" "strings" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/zettel/meta" ) // parseInlineSlice parses a sequence of Inlines until EOS. func (cp *zmkP) parseInlineSlice() (ins ast.InlineSlice) { inp := cp.inp for inp.Ch != input.EOS { |
︙ | ︙ |
Changes to parser/zettelmark/node.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettelmark import "zettelstore.de/z/ast" // Internal nodes for parsing zettelmark. These will be removed in |
︙ | ︙ |
Changes to parser/zettelmark/post-processor.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettelmark import ( "strings" |
︙ | ︙ | |||
130 131 132 133 134 135 136 | } } } func (pp *postProcessor) visitTable(tn *ast.TableNode) { width := tableWidth(tn) tn.Align = make([]ast.Alignment, width) | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | } } } func (pp *postProcessor) visitTable(tn *ast.TableNode) { width := tableWidth(tn) tn.Align = make([]ast.Alignment, width) for i := 0; i < width; i++ { tn.Align[i] = ast.AlignDefault } if len(tn.Rows) > 0 && isHeaderRow(tn.Rows[0]) { tn.Header = tn.Rows[0] tn.Rows = tn.Rows[1:] pp.visitTableHeader(tn) } |
︙ | ︙ |
Changes to parser/zettelmark/zettelmark.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package zettelmark provides a parser for zettelmarkup. package zettelmark import ( "strings" "unicode" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func init() { parser.Register(&parser.Info{ Name: meta.SyntaxZmk, |
︙ | ︙ |
Changes to parser/zettelmark/zettelmark_fuzz_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package zettelmark_test import ( "testing" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) func FuzzParseBlocks(f *testing.F) { f.Fuzz(func(t *testing.T, src []byte) { t.Parallel() |
︙ | ︙ |
Changes to parser/zettelmark/zettelmark_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package zettelmark_test provides some tests for the zettelmarkup parser. package zettelmark_test import ( "fmt" "strings" "testing" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) type TestCase struct{ source, want string } type TestCases []TestCase |
︙ | ︙ |
Changes to query/compiled.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "math/rand" "sort" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Compiled is a compiled query, to be used in a Box |
︙ | ︙ | |||
138 139 140 141 142 143 144 | } if limit := c.limit; limit > 0 && limit < count { count = limit c.limit = 0 } order := make([]int, len(metaList)) | | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | } if limit := c.limit; limit > 0 && limit < count { count = limit c.limit = 0 } order := make([]int, len(metaList)) for i := 0; i < len(metaList); i++ { order[i] = i } rnd := c.newRandom() picked := make([]int, count) for i := 0; i < count; i++ { last := len(order) - i n := rnd.Intn(last) picked[i] = order[n] order[n] = order[last-1] } order = nil sort.Ints(picked) result := make([]*meta.Meta, count) for i, p := range picked { |
︙ | ︙ | |||
170 171 172 173 174 175 176 | ) return metaList } func (c *Compiled) newRandom() *rand.Rand { seed := c.seed if seed <= 0 { | | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | ) return metaList } func (c *Compiled) newRandom() *rand.Rand { seed := c.seed if seed <= 0 { seed = rand.Intn(10000) + 10001 } return rand.New(rand.NewSource(int64(seed))) } func limitElements(metaList []*meta.Meta, limit int) []*meta.Meta { if limit > 0 && limit < len(metaList) { return metaList[:limit] } return metaList } func sortMetaByZid(metaList []*meta.Meta) []*meta.Meta { sort.Slice(metaList, func(i, j int) bool { return metaList[i].Zid > metaList[j].Zid }) return metaList } |
Changes to query/context.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "container/heap" "context" "zettelstore.de/client.fossil/api" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // ContextSpec contains all specification values for calculating a context. type ContextSpec struct { Direction ContextDirection MaxCost int MaxCount int } // ContextDirection specifies the direction a context should be calculated. type ContextDirection uint8 const ( ContextDirBoth ContextDirection = iota ContextDirForward ContextDirBackward ) // ContextPort is the collection of box methods needed by this directive. type ContextPort interface { GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error) SelectMeta(ctx context.Context, metaSeq []*meta.Meta, q *Query) ([]*meta.Meta, error) } func (spec *ContextSpec) Print(pe *PrintEnv) { pe.printSpace() pe.writeString(api.ContextDirective) switch spec.Direction { case ContextDirBackward: pe.printSpace() pe.writeString(api.BackwardDirective) case ContextDirForward: pe.printSpace() pe.writeString(api.ForwardDirective) } pe.printPosInt(api.CostDirective, spec.MaxCost) pe.printPosInt(api.MaxDirective, spec.MaxCount) } func (spec *ContextSpec) Execute(ctx context.Context, startSeq []*meta.Meta, port ContextPort) []*meta.Meta { maxCost := spec.MaxCost if maxCost <= 0 { maxCost = 17 } maxCount := spec.MaxCount if maxCount <= 0 { maxCount = 200 } |
︙ | ︙ | |||
84 85 86 87 88 89 90 | break } result = append(result, m) for _, p := range m.ComputedPairsRest() { tasks.addPair(ctx, p.Key, p.Value, cost, isBackward, isForward) } | < < < > | > | | | | | | | < < | | | | | | < < | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | break } result = append(result, m) for _, p := range m.ComputedPairsRest() { tasks.addPair(ctx, p.Key, p.Value, cost, isBackward, isForward) } if tags, found := m.GetList(api.KeyTags); found { for _, tag := range tags { tasks.addSameTag(ctx, tag, cost) } } } return result } type ztlCtxItem struct { cost int meta *meta.Meta } type ztlCtxQueue []ztlCtxItem func (q ztlCtxQueue) Len() int { return len(q) } func (q ztlCtxQueue) Less(i, j int) bool { return q[i].cost < q[j].cost } func (q ztlCtxQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } func (q *ztlCtxQueue) Push(x any) { *q = append(*q, x.(ztlCtxItem)) } func (q *ztlCtxQueue) Pop() any { old := *q n := len(old) item := old[n-1] old[n-1].meta = nil // avoid memory leak *q = old[0 : n-1] return item } type contextTask struct { port ContextPort seen id.Set queue ztlCtxQueue maxCost int limit int tagCost map[string][]*meta.Meta } func newQueue(startSeq []*meta.Meta, maxCost, limit int, port ContextPort) *contextTask { result := &contextTask{ port: port, seen: id.NewSet(), maxCost: maxCost, limit: limit, tagCost: make(map[string][]*meta.Meta, 1024), } queue := make(ztlCtxQueue, 0, len(startSeq)) for _, m := range startSeq { queue = append(queue, ztlCtxItem{cost: 1, meta: m}) } heap.Init(&queue) result.queue = queue return result } func (ct *contextTask) addPair(ctx context.Context, key, value string, curCost int, isBackward, isForward bool) { if key == api.KeyBack { return } newCost := curCost + contextCost(key) if key == api.KeyBackward { if isBackward { ct.addIDSet(ctx, newCost, value) |
︙ | ︙ | |||
172 173 174 175 176 177 178 | if t := meta.Type(key); t == meta.TypeID { ct.addID(ctx, newCost, value) } else if t == meta.TypeIDSet { ct.addIDSet(ctx, newCost, value) } } | | < < | > | | | > > > > > > | > > | | < < | | < < | | > | | < | < < < < < < < | | < < < | | | < > | | < | | < < < < < | > > > | | | < > | | < | > | < | | | > > < < | | | | > | > | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | if t := meta.Type(key); t == meta.TypeID { ct.addID(ctx, newCost, value) } else if t == meta.TypeIDSet { ct.addIDSet(ctx, newCost, value) } } func contextCost(key string) int { switch key { case api.KeyFolge, api.KeyPrecursor: return 1 case api.KeySuccessors, api.KeyPredecessor, api.KeySubordinates, api.KeySuperior: return 2 } return 3 } func (ct *contextTask) addID(ctx context.Context, newCost int, value string) { if ct.costMaxed(newCost) { return } if zid, errParse := id.Parse(value); errParse == nil { if m, errGetMeta := ct.port.GetMeta(ctx, zid); errGetMeta == nil { ct.addMeta(m, newCost) } } } func (ct *contextTask) addMeta(m *meta.Meta, newCost int) { if _, found := ct.seen[m.Zid]; !found { heap.Push(&ct.queue, ztlCtxItem{cost: newCost, meta: m}) } } func (ct *contextTask) costMaxed(newCost int) bool { // If len(zc.seen) <= 1, the initial zettel is processed. In this case allow all // other zettel that are directly reachable, without taking the cost into account. // Of course, the limit ist still relevant. return (len(ct.seen) > 1 && ct.maxCost > 0 && newCost > ct.maxCost) || ct.hasLimit() } func (ct *contextTask) addIDSet(ctx context.Context, newCost int, value string) { elems := meta.ListFromValue(value) refCost := referenceCost(newCost, len(elems)) for _, val := range elems { ct.addID(ctx, refCost, val) } } func referenceCost(baseCost int, numReferences int) int { switch { case numReferences < 5: return baseCost + 1 case numReferences < 9: return baseCost * 2 case numReferences < 17: return baseCost * 3 case numReferences < 33: return baseCost * 4 case numReferences < 65: return baseCost * 5 } return baseCost * numReferences / 8 } func (ct *contextTask) addSameTag(ctx context.Context, tag string, baseCost int) { tagMetas, found := ct.tagCost[tag] if !found { q := Parse(api.KeyTags + api.SearchOperatorHas + tag + " ORDER REVERSE " + api.KeyID) ml, err := ct.port.SelectMeta(ctx, nil, q) if err != nil { return } tagMetas = ml ct.tagCost[tag] = ml } cost := tagCost(baseCost, len(tagMetas)) if ct.costMaxed(cost) { return } for _, m := range tagMetas { ct.addMeta(m, cost) } } func tagCost(baseCost, numTags int) int { if numTags < 8 { return baseCost + numTags/2 } return (baseCost + 2) * (numTags / 4) } func (ct *contextTask) next() (*meta.Meta, int) { if ct.hasLimit() { return nil, -1 } for len(ct.queue) > 0 { item := heap.Pop(&ct.queue).(ztlCtxItem) m := item.meta zid := m.Zid |
︙ | ︙ |
Changes to query/parser.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "strconv" "zettelstore.de/client.fossil/api" "zettelstore.de/z/input" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Parse the query specification and return a Query object. func Parse(spec string) (q *Query) { return q.Parse(spec) } |
︙ | ︙ | |||
204 205 206 207 208 209 210 | spec := &ContextSpec{} for { ps.skipSpace() if ps.mustStop() { break } pos := inp.Pos | < < < < < | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | spec := &ContextSpec{} for { ps.skipSpace() if ps.mustStop() { break } pos := inp.Pos if ps.acceptSingleKw(api.BackwardDirective) { spec.Direction = ContextDirBackward continue } inp.SetPos(pos) if ps.acceptSingleKw(api.ForwardDirective) { spec.Direction = ContextDirForward |
︙ | ︙ |
Changes to query/parser_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package query_test import ( "testing" |
︙ | ︙ | |||
34 35 36 37 38 39 40 | {"1 ITEMS", "00000000000001 ITEMS"}, {"ITEMS", "ITEMS"}, {"CONTEXT", "CONTEXT"}, {"CONTEXT a", "CONTEXT a"}, {"0 CONTEXT", "0 CONTEXT"}, {"1 CONTEXT", "00000000000001 CONTEXT"}, {"00000000000001 CONTEXT", "00000000000001 CONTEXT"}, {"100000000000001 CONTEXT", "100000000000001 CONTEXT"}, | < | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | {"1 ITEMS", "00000000000001 ITEMS"}, {"ITEMS", "ITEMS"}, {"CONTEXT", "CONTEXT"}, {"CONTEXT a", "CONTEXT a"}, {"0 CONTEXT", "0 CONTEXT"}, {"1 CONTEXT", "00000000000001 CONTEXT"}, {"00000000000001 CONTEXT", "00000000000001 CONTEXT"}, {"100000000000001 CONTEXT", "100000000000001 CONTEXT"}, {"1 CONTEXT BACKWARD", "00000000000001 CONTEXT BACKWARD"}, {"1 CONTEXT FORWARD", "00000000000001 CONTEXT FORWARD"}, {"1 CONTEXT COST ", "00000000000001 CONTEXT COST"}, {"1 CONTEXT COST 3", "00000000000001 CONTEXT COST 3"}, {"1 CONTEXT COST x", "00000000000001 CONTEXT COST x"}, {"1 CONTEXT MAX 5", "00000000000001 CONTEXT MAX 5"}, {"1 CONTEXT MAX y", "00000000000001 CONTEXT MAX y"}, {"1 CONTEXT MAX 5 COST 7", "00000000000001 CONTEXT COST 7 MAX 5"}, {"1 CONTEXT | N", "00000000000001 CONTEXT | N"}, |
︙ | ︙ |
Changes to query/print.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "io" "strconv" |
︙ | ︙ |
Changes to query/query.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package query provides a query for zettel. package query import ( "context" "math/rand" "slices" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Searcher is used to select zettel identifier based on search criteria. |
︙ | ︙ | |||
246 247 248 249 250 251 252 | func (q *Query) addKey(key string, op compareOp) *Query { q = createIfNeeded(q) q.terms[len(q.terms)-1].addKey(key, op) return q } | < < < < < < < < < < | < | < | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | func (q *Query) addKey(key string, op compareOp) *Query { q = createIfNeeded(q) q.terms[len(q.terms)-1].addKey(key, op) return q } func (q *Query) GetMetaValues(key string) (vals []string) { if q == nil { return nil } for _, term := range q.terms { if mvs, hasMv := term.mvals[key]; hasMv { for _, ev := range mvs { vals = append(vals, ev.value) } } } slices.Sort(vals) return slices.Compact(vals) } |
︙ | ︙ | |||
302 303 304 305 306 307 308 | return q.seed, q.seed > 0 } // SetDeterministic signals that the result should be the same if the seed is the same. func (q *Query) SetDeterministic() *Query { q = createIfNeeded(q) if q.seed <= 0 { | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | return q.seed, q.seed > 0 } // SetDeterministic signals that the result should be the same if the seed is the same. func (q *Query) SetDeterministic() *Query { q = createIfNeeded(q) if q.seed <= 0 { q.seed = int(rand.Intn(10000) + 1) } return q } // Actions returns the slice of action specifications func (q *Query) Actions() []string { if q == nil { |
︙ | ︙ |
Changes to query/retrieve.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package query // This file contains helper functions to search within the index. import ( |
︙ | ︙ |
Changes to query/select.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "fmt" "strconv" |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | return createMatchTimestampFunc(values, addSearch) case meta.TypeNumber: return createMatchNumberFunc(values, addSearch) case meta.TypeTagSet: return createMatchTagSetFunc(values, addSearch) case meta.TypeWord: return createMatchWordFunc(values, addSearch) case meta.TypeZettelmarkup: return createMatchZmkFunc(values, addSearch) } return createMatchStringFunc(values, addSearch) } func createMatchIDFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { preds := valuesToIDPredicates(values, addSearch) return func(value string) bool { for _, pred := range preds { if !pred(value) { return false } } return true } } func createMatchIDSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { | > > | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | return createMatchTimestampFunc(values, addSearch) case meta.TypeNumber: return createMatchNumberFunc(values, addSearch) case meta.TypeTagSet: return createMatchTagSetFunc(values, addSearch) case meta.TypeWord: return createMatchWordFunc(values, addSearch) case meta.TypeWordSet: return createMatchWordSetFunc(values, addSearch) case meta.TypeZettelmarkup: return createMatchZmkFunc(values, addSearch) } return createMatchStringFunc(values, addSearch) } func createMatchIDFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { preds := valuesToIDPredicates(values, addSearch) return func(value string) bool { for _, pred := range preds { if !pred(value) { return false } } return true } } func createMatchIDSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { predList := valuesToWordSetPredicates(preprocessSet(values), addSearch) return func(value string) bool { ids := meta.ListFromValue(value) for _, preds := range predList { for _, pred := range preds { if !pred(ids) { return false } |
︙ | ︙ | |||
178 179 180 181 182 183 184 | } } return true } } func createMatchTagSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | } } return true } } func createMatchTagSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { predList := valuesToWordSetPredicates(processTagSet(preprocessSet(sliceToLower(values))), addSearch) return func(value string) bool { tags := meta.TagsFromValue(value) for _, preds := range predList { for _, pred := range preds { if !pred(tags) { return false } |
︙ | ︙ | |||
230 231 232 233 234 235 236 237 238 239 240 241 242 243 | preds := valuesToStringPredicates(sliceToLower(values), addSearch) return func(value string) bool { value = strings.ToLower(value) for _, pred := range preds { if !pred(value) { return false } } return true } } func sliceToLower(sl []expValue) []expValue { result := make([]expValue, 0, len(sl)) | > > > > > > > > > > > > > > > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | preds := valuesToStringPredicates(sliceToLower(values), addSearch) return func(value string) bool { value = strings.ToLower(value) for _, pred := range preds { if !pred(value) { return false } } return true } } func createMatchWordSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc { predsList := valuesToWordSetPredicates(preprocessSet(sliceToLower(values)), addSearch) return func(value string) bool { words := meta.ListFromValue(value) for _, preds := range predsList { for _, pred := range preds { if !pred(words) { return false } } } return true } } func sliceToLower(sl []expValue) []expValue { result := make([]expValue, 0, len(sl)) |
︙ | ︙ | |||
293 294 295 296 297 298 299 | } } return true } func zmk2text(zmk string) string { isASCII, hasUpper, needParse := true, false, false | | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | } } return true } func zmk2text(zmk string) string { isASCII, hasUpper, needParse := true, false, false for i := 0; i < len(zmk); i++ { ch := zmk[i] if ch >= utf8.RuneSelf { isASCII = false break } hasUpper = hasUpper || ('A' <= ch && ch <= 'Z') needParse = needParse || !(('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9') || ch == ' ') |
︙ | ︙ | |||
368 369 370 371 372 373 374 | result[i] = createWordCompareFunc(value, op) } } return result } func isDigits(s string) bool { | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | result[i] = createWordCompareFunc(value, op) } } return result } func isDigits(s string) bool { for i := 0; i < len(s); i++ { if ch := s[i]; ch < '0' || '9' < ch { return false } } return true } |
︙ | ︙ | |||
548 549 550 551 552 553 554 | default: panic(fmt.Sprintf("Unknown compare operation %d with value %q", cmpOp, cmpVal)) } } type stringSetPredicate func(value []string) bool | | | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | default: panic(fmt.Sprintf("Unknown compare operation %d with value %q", cmpOp, cmpVal)) } } type stringSetPredicate func(value []string) bool func valuesToWordSetPredicates(values [][]expValue, addSearch addSearchFunc) [][]stringSetPredicate { result := make([][]stringSetPredicate, len(values)) for i, val := range values { elemPreds := make([]stringSetPredicate, len(val)) for j, v := range val { opVal := v.value // loop variable is used in closure --> save needed value switch op := disambiguateWordOp(v.op); op { case cmpEqual: |
︙ | ︙ |
Changes to query/select_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package query_test import ( "context" "testing" |
︙ | ︙ |
Changes to query/sorter.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "strconv" |
︙ | ︙ |
Changes to query/specs.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package query import "zettelstore.de/client.fossil/api" // IdentSpec contains all specification values to calculate the ident directive. |
︙ | ︙ |
Changes to query/unlinked.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package query import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/strfun" |
︙ | ︙ |
Changes to strfun/escape.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package strfun import "io" var ( |
︙ | ︙ |
Changes to strfun/set.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package strfun // Set ist a set of strings. type Set map[string]struct{} |
︙ | ︙ |
Changes to strfun/slugify.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package strfun import ( "strings" "unicode" |
︙ | ︙ |
Changes to strfun/slugify_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package strfun_test import ( "testing" |
︙ | ︙ |
Changes to strfun/strfun.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package strfun provides some string functions. package strfun import ( "strings" |
︙ | ︙ | |||
39 40 41 42 43 44 45 | runes[maxLen-1] = '\u2025' } var sb strings.Builder for _, r := range runes { sb.WriteRune(r) } | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | runes[maxLen-1] = '\u2025' } var sb strings.Builder for _, r := range runes { sb.WriteRune(r) } for i := 0; i < maxLen-len(runes); i++ { sb.WriteRune(pad) } return sb.String() } // SplitLines splits the given string into a list of lines. func SplitLines(s string) []string { |
︙ | ︙ |
Changes to strfun/strfun_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package strfun_test import ( "testing" |
︙ | ︙ |
Changes to tests/client/client_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. | < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. //----------------------------------------------------------------------------- // Package client provides a client for accessing the Zettelstore via its API. package client_test import ( "context" "flag" "fmt" "net/http" "net/url" "strconv" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/client" "zettelstore.de/z/kernel" ) |
︙ | ︙ | |||
420 421 422 423 424 425 426 | if err != nil { t.Error(err) } else if zid != exp { t.Errorf("role zettel for zettel should be %q, but got %q", exp, zid) } } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | if err != nil { t.Error(err) } else if zid != exp { t.Errorf("role zettel for zettel should be %q, but got %q", exp, zid) } } func TestVersion(t *testing.T) { t.Parallel() c := getClient() ver, err := c.GetVersionInfo(context.Background()) if err != nil { t.Error(err) return |
︙ | ︙ |
Changes to tests/client/crud_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. //----------------------------------------------------------------------------- package client_test import ( "context" "strings" |
︙ | ︙ |
Changes to tests/client/embed_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package client_test import ( "context" "strings" |
︙ | ︙ |
Changes to tests/markdown_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package tests import ( "bytes" "encoding/json" "fmt" "os" "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/encoder" _ "zettelstore.de/z/encoder/htmlenc" _ "zettelstore.de/z/encoder/mdenc" _ "zettelstore.de/z/encoder/shtmlenc" _ "zettelstore.de/z/encoder/szenc" _ "zettelstore.de/z/encoder/textenc" _ "zettelstore.de/z/encoder/zmkenc" "zettelstore.de/z/input" "zettelstore.de/z/parser" _ "zettelstore.de/z/parser/markdown" _ "zettelstore.de/z/parser/zettelmark" "zettelstore.de/z/zettel/meta" ) type markdownTestCase struct { |
︙ | ︙ |
Changes to tests/naughtystrings_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package tests import ( "bufio" "io" "os" "path/filepath" "testing" "zettelstore.de/client.fossil/api" _ "zettelstore.de/z/cmd" "zettelstore.de/z/encoder" "zettelstore.de/z/input" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) // Test all parser / encoder with a list of "naughty strings", i.e. unusual strings // that often crash software. |
︙ | ︙ |
Changes to tests/regression_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package tests provides some higher-level tests. package tests import ( "context" |
︙ | ︙ |
Added tools/build.go.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package main provides a command to build and run the software. package main import ( "archive/zip" "bytes" "errors" "flag" "fmt" "io" "io/fs" "net" "os" "os/exec" "path/filepath" "strings" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/z/input" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) var envDirectProxy = []string{"GOPROXY=direct"} var envGoVCS = []string{"GOVCS=zettelstore.de:fossil"} func executeCommand(env []string, name string, arg ...string) (string, error) { logCommand("EXEC", env, name, arg) var out strings.Builder cmd := prepareCommand(env, name, arg, &out) err := cmd.Run() return out.String(), err } func prepareCommand(env []string, name string, arg []string, out io.Writer) *exec.Cmd { if len(env) > 0 { env = append(env, os.Environ()...) } cmd := exec.Command(name, arg...) cmd.Env = env cmd.Stdin = nil cmd.Stdout = out cmd.Stderr = os.Stderr return cmd } func logCommand(exec string, env []string, name string, arg []string) { if verbose { if len(env) > 0 { for i, e := range env { fmt.Fprintf(os.Stderr, "ENV%d %v\n", i+1, e) } } fmt.Fprintln(os.Stderr, exec, name, arg) } } func readVersionFile() (string, error) { content, err := os.ReadFile("VERSION") if err != nil { return "", err } return strings.TrimFunc(string(content), func(r rune) bool { return r <= ' ' }), nil } func getVersion() string { base, err := readVersionFile() if err != nil { base = "dev" } return base } var dirtyPrefixes = []string{ "DELETED ", "ADDED ", "UPDATED ", "CONFLICT ", "EDITED ", "RENAMED ", "EXTRA "} const dirtySuffix = "-dirty" func readFossilDirty() (string, error) { s, err := executeCommand(nil, "fossil", "status", "--differ") if err != nil { return "", err } for _, line := range strfun.SplitLines(s) { for _, prefix := range dirtyPrefixes { if strings.HasPrefix(line, prefix) { return dirtySuffix, nil } } } return "", nil } func getFossilDirty() string { fossil, err := readFossilDirty() if err != nil { return "" } return fossil } func findExec(cmd string) string { if path, err := executeCommand(nil, "which", cmd); err == nil && path != "" { return strings.TrimSpace(path) } return "" } func cmdCheck(forRelease bool) error { if err := checkGoTest("./..."); err != nil { return err } if err := checkGoVet(); err != nil { return err } if err := checkShadow(forRelease); err != nil { return err } if err := checkStaticcheck(); err != nil { return err } if err := checkUnparam(forRelease); err != nil { return err } if forRelease { if err := checkGoVulncheck(); err != nil { return err } } return checkFossilExtra() } func checkGoTest(pkg string, testParams ...string) error { var env []string env = append(env, envDirectProxy...) env = append(env, envGoVCS...) args := []string{"test", pkg} args = append(args, testParams...) out, err := executeCommand(env, "go", args...) if err != nil { for _, line := range strfun.SplitLines(out) { if strings.HasPrefix(line, "ok") || strings.HasPrefix(line, "?") { continue } fmt.Fprintln(os.Stderr, line) } } return err } func checkGoVet() error { out, err := executeCommand(envGoVCS, "go", "vet", "./...") if err != nil { fmt.Fprintln(os.Stderr, "Some checks failed") if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } return err } func checkShadow(forRelease bool) error { path, err := findExecStrict("shadow", forRelease) if path == "" { return err } out, err := executeCommand(envGoVCS, path, "-strict", "./...") if err != nil { fmt.Fprintln(os.Stderr, "Some shadowed variables found") if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } return err } func checkStaticcheck() error { out, err := executeCommand(envGoVCS, "staticcheck", "./...") if err != nil { fmt.Fprintln(os.Stderr, "Some staticcheck problems found") if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } return err } func checkUnparam(forRelease bool) error { path, err := findExecStrict("unparam", forRelease) if path == "" { return err } out, err := executeCommand(envGoVCS, path, "./...") if err != nil { fmt.Fprintln(os.Stderr, "Some unparam problems found") if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } if forRelease { if out2, err2 := executeCommand(nil, path, "-exported", "-tests", "./..."); err2 != nil { fmt.Fprintln(os.Stderr, "Some optional unparam problems found") if len(out2) > 0 { fmt.Fprintln(os.Stderr, out2) } } } return err } func checkGoVulncheck() error { out, err := executeCommand(envGoVCS, "govulncheck", "./...") if err != nil { fmt.Fprintln(os.Stderr, "Some checks failed") if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } return err } func findExecStrict(cmd string, forRelease bool) (string, error) { path := findExec(cmd) if path != "" || !forRelease { return path, nil } return "", errors.New("Command '" + cmd + "' not installed, but required for release") } func checkFossilExtra() error { out, err := executeCommand(nil, "fossil", "extra") if err != nil { fmt.Fprintln(os.Stderr, "Unable to execute 'fossil extra'") return err } if len(out) > 0 { fmt.Fprint(os.Stderr, "Warning: unversioned file(s):") for i, extra := range strfun.SplitLines(out) { if i > 0 { fmt.Fprint(os.Stderr, ",") } fmt.Fprintf(os.Stderr, " %q", extra) } fmt.Fprintln(os.Stderr) } return nil } type zsInfo struct { cmd *exec.Cmd out strings.Builder adminAddress string } func cmdTestAPI() error { var err error var info zsInfo needServer := !addressInUse(":23123") if needServer { err = startZettelstore(&info) } if err != nil { return err } err = checkGoTest("zettelstore.de/z/tests/client", "-base-url", "http://127.0.0.1:23123") if needServer { err1 := stopZettelstore(&info) if err == nil { err = err1 } } return err } func startZettelstore(info *zsInfo) error { info.adminAddress = ":2323" name, arg := "go", []string{ "run", "cmd/zettelstore/main.go", "run", "-c", "./testdata/testbox/19700101000000.zettel", "-a", info.adminAddress[1:]} logCommand("FORK", nil, name, arg) cmd := prepareCommand(envGoVCS, name, arg, &info.out) if !verbose { cmd.Stderr = nil } err := cmd.Start() time.Sleep(2 * time.Second) for i := 0; i < 100; i++ { time.Sleep(time.Millisecond * 100) if addressInUse(info.adminAddress) { info.cmd = cmd return err } } time.Sleep(4 * time.Second) // Wait for all zettel to be indexed. return errors.New("zettelstore did not start") } func stopZettelstore(i *zsInfo) error { conn, err := net.Dial("tcp", i.adminAddress) if err != nil { fmt.Println("Unable to stop Zettelstore") return err } io.WriteString(conn, "shutdown\n") conn.Close() err = i.cmd.Wait() return err } func addressInUse(address string) bool { conn, err := net.Dial("tcp", address) if err != nil { return false } conn.Close() return true } func cmdBuild() error { return doBuild(envDirectProxy, getVersion(), "bin/zettelstore") } func doBuild(env []string, version, target string) error { env = append(env, "CGO_ENABLED=0") env = append(env, envGoVCS...) out, err := executeCommand( env, "go", "build", "-tags", "osusergo,netgo", "-trimpath", "-ldflags", fmt.Sprintf("-X main.version=%v -w", version), "-o", target, "zettelstore.de/z/cmd/zettelstore", ) if err != nil { return err } if len(out) > 0 { fmt.Println(out) } return nil } func cmdManual() error { base := getReleaseVersionData() return createManualZip(".", base) } func createManualZip(path, base string) error { manualPath := filepath.Join("docs", "manual") entries, err := os.ReadDir(manualPath) if err != nil { return err } zipName := filepath.Join(path, "manual-"+base+".zip") zipFile, err := os.OpenFile(zipName, os.O_RDWR|os.O_CREATE, 0600) if err != nil { return err } defer zipFile.Close() zipWriter := zip.NewWriter(zipFile) defer zipWriter.Close() for _, entry := range entries { if err = createManualZipEntry(manualPath, entry, zipWriter); err != nil { return err } } return nil } const versionZid = "00001000000001" func createManualZipEntry(path string, entry fs.DirEntry, zipWriter *zip.Writer) error { info, err := entry.Info() if err != nil { return err } fh, err := zip.FileInfoHeader(info) if err != nil { return err } name := entry.Name() fh.Name = name fh.Method = zip.Deflate w, err := zipWriter.CreateHeader(fh) if err != nil { return err } manualFile, err := os.Open(filepath.Join(path, name)) if err != nil { return err } defer manualFile.Close() if name != versionZid+".zettel" { _, err = io.Copy(w, manualFile) return err } data, err := io.ReadAll(manualFile) if err != nil { return err } inp := input.NewInput(data) m := meta.NewFromInput(id.MustParse(versionZid), inp) m.SetNow(api.KeyModified) var buf bytes.Buffer if _, err = fmt.Fprintf(&buf, "id: %s\n", versionZid); err != nil { return err } if _, err = m.WriteComputed(&buf); err != nil { return err } version := getVersion() if _, err = fmt.Fprintf(&buf, "\n%s", version); err != nil { return err } _, err = io.Copy(w, &buf) return err } func getReleaseVersionData() string { if fossil := getFossilDirty(); fossil != "" { fmt.Fprintln(os.Stderr, "Warning: releasing a dirty version") } base := getVersion() if strings.HasSuffix(base, "dev") { return base[:len(base)-3] + "preview-" + time.Now().Local().Format("20060102") } return base } func cmdRelease() error { if err := cmdCheck(true); err != nil { return err } base := getReleaseVersionData() releases := []struct { arch string os string env []string name string }{ {"amd64", "linux", nil, "zettelstore"}, {"arm", "linux", []string{"GOARM=6"}, "zettelstore"}, {"amd64", "darwin", nil, "zettelstore"}, {"arm64", "darwin", nil, "zettelstore"}, {"amd64", "windows", nil, "zettelstore.exe"}, } for _, rel := range releases { env := append([]string{}, rel.env...) env = append(env, "GOARCH="+rel.arch, "GOOS="+rel.os) env = append(env, envDirectProxy...) env = append(env, envGoVCS...) zsName := filepath.Join("releases", rel.name) if err := doBuild(env, base, zsName); err != nil { return err } zipName := fmt.Sprintf("zettelstore-%v-%v-%v.zip", base, rel.os, rel.arch) if err := createReleaseZip(zsName, zipName, rel.name); err != nil { return err } if err := os.Remove(zsName); err != nil { return err } } return createManualZip("releases", base) } func createReleaseZip(zsName, zipName, fileName string) error { zipFile, err := os.OpenFile(filepath.Join("releases", zipName), os.O_RDWR|os.O_CREATE, 0600) if err != nil { return err } defer zipFile.Close() zw := zip.NewWriter(zipFile) defer zw.Close() err = addFileToZip(zw, zsName, fileName) if err != nil { return err } err = addFileToZip(zw, "LICENSE.txt", "LICENSE.txt") if err != nil { return err } err = addFileToZip(zw, "docs/readmezip.txt", "README.txt") return err } func addFileToZip(zipFile *zip.Writer, filepath, filename string) error { zsFile, err := os.Open(filepath) if err != nil { return err } defer zsFile.Close() stat, err := zsFile.Stat() if err != nil { return err } fh, err := zip.FileInfoHeader(stat) if err != nil { return err } fh.Name = filename fh.Method = zip.Deflate w, err := zipFile.CreateHeader(fh) if err != nil { return err } _, err = io.Copy(w, zsFile) return err } func cmdTools() error { tools := []struct{ name, pack string }{ {"shadow", "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest"}, {"unparam", "mvdan.cc/unparam@latest"}, {"staticcheck", "honnef.co/go/tools/cmd/staticcheck@latest"}, {"govulncheck", "golang.org/x/vuln/cmd/govulncheck@latest"}, } for _, tool := range tools { err := doGoInstall(tool.pack) if err != nil { return err } } return nil } func doGoInstall(pack string) error { out, err := executeCommand(nil, "go", "install", pack) if err != nil { fmt.Fprintln(os.Stderr, "Unable to install package", pack) if len(out) > 0 { fmt.Fprintln(os.Stderr, out) } } return err } func cmdClean() error { for _, dir := range []string{"bin", "releases"} { err := os.RemoveAll(dir) if err != nil { return err } } out, err := executeCommand(nil, "go", "clean", "./...") if err != nil { return err } if len(out) > 0 { fmt.Println(out) } out, err = executeCommand(nil, "go", "clean", "-cache", "-modcache", "-testcache") if err != nil { return err } if len(out) > 0 { fmt.Println(out) } return nil } func cmdHelp() { fmt.Println(`Usage: go run tools/build.go [-v] COMMAND Options: -v Verbose output. Commands: build Build the software for local computer. check Check current working state: execute tests, static analysis tools, extra files, ... Is automatically done when releasing the software. clean Remove all build and release directories. help Output this text. manual Create a ZIP file with all manual zettel relcheck Check current working state for release. release Create the software for various platforms and put them in appropriate named ZIP files. testapi Start a Zettelstore and execute API tests. tools Install/update tools needed for building Zettelstore. version Print the current version of the software. All commands can be abbreviated as long as they remain unique.`) } var verbose bool func main() { flag.BoolVar(&verbose, "v", false, "Verbose output") flag.Parse() var err error args := flag.Args() if len(args) < 1 { cmdHelp() } else { switch args[0] { case "b", "bu", "bui", "buil", "build": err = cmdBuild() case "m", "ma", "man", "manu", "manua", "manual": err = cmdManual() case "r", "re", "rel", "rele", "relea", "releas", "release": err = cmdRelease() case "cl", "cle", "clea", "clean": err = cmdClean() case "v", "ve", "ver", "vers", "versi", "versio", "version": fmt.Print(getVersion()) case "ch", "che", "chec", "check": err = cmdCheck(false) case "relc", "relch", "relche", "relchec", "relcheck": err = cmdCheck(true) case "te", "tes", "test", "testa", "testap", "testapi": cmdTestAPI() case "to", "too", "tool", "tools": err = cmdTools() case "h", "he", "hel", "help": cmdHelp() default: fmt.Fprintf(os.Stderr, "Unknown command %q\n", args[0]) cmdHelp() os.Exit(1) } } if err != nil { fmt.Fprintln(os.Stderr, err) } } |
Deleted tools/build/build.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/check/check.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/clean/clean.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/devtools/devtools.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/htmllint/htmllint.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/testapi/testapi.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted tools/tools.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to usecase/authenticate.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" "math/rand" "net/http" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" "zettelstore.de/z/auth/cred" "zettelstore.de/z/logger" |
︙ | ︙ | |||
86 87 88 89 90 91 92 | "$2a$10$WHcSO3G9afJ3zlOYQR1suuf83bCXED2jmzjti/MH4YH4l2mivDuze", id.Invalid, "", "") } // addDelay after credential checking to allow some CPU time for other tasks. // durDelay is the normal delay, if time spend for checking is smaller than // the minimum delay minDelay. In addition some jitter (+/- 50 ms) is added. func addDelay(start time.Time, durDelay, minDelay time.Duration) { | | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | "$2a$10$WHcSO3G9afJ3zlOYQR1suuf83bCXED2jmzjti/MH4YH4l2mivDuze", id.Invalid, "", "") } // addDelay after credential checking to allow some CPU time for other tasks. // durDelay is the normal delay, if time spend for checking is smaller than // the minimum delay minDelay. In addition some jitter (+/- 50 ms) is added. func addDelay(start time.Time, durDelay, minDelay time.Duration) { jitter := time.Duration(rand.Intn(100)-50) * time.Millisecond if elapsed := time.Since(start); elapsed+minDelay < durDelay { time.Sleep(durDelay - elapsed + jitter) } else { time.Sleep(minDelay + jitter) } } |
︙ | ︙ | |||
129 130 131 132 133 134 135 | IsAuthenticatedAndValid IsAuthenticatedAndInvalid ) // Run executes the use case. func (uc *IsAuthenticated) Run(ctx context.Context) IsAuthenticatedResult { if !uc.authz.WithAuth() { | | | | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | IsAuthenticatedAndValid IsAuthenticatedAndInvalid ) // Run executes the use case. func (uc *IsAuthenticated) Run(ctx context.Context) IsAuthenticatedResult { if !uc.authz.WithAuth() { uc.log.Sense().Str("auth", "disabled").Msg("IsAuthenticated") return IsAuthenticatedDisabled } if uc.port.GetUser(ctx) == nil { uc.log.Sense().Msg("IsAuthenticated is false") return IsAuthenticatedAndInvalid } uc.log.Sense().Msg("IsAuthenticated is true") return IsAuthenticatedAndValid } |
Changes to usecase/create_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" "time" |
︙ | ︙ | |||
115 116 117 118 119 120 121 | content.TrimSpace() return zettel.Zettel{Meta: m, Content: content} } func updateMetaRoleTagsSyntax(m, orig *meta.Meta) { m.SetNonEmpty(api.KeyRole, orig.GetDefault(api.KeyRole, "")) m.SetNonEmpty(api.KeyTags, orig.GetDefault(api.KeyTags, "")) | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | content.TrimSpace() return zettel.Zettel{Meta: m, Content: content} } func updateMetaRoleTagsSyntax(m, orig *meta.Meta) { m.SetNonEmpty(api.KeyRole, orig.GetDefault(api.KeyRole, "")) m.SetNonEmpty(api.KeyTags, orig.GetDefault(api.KeyTags, "")) m.SetNonEmpty(api.KeySyntax, orig.GetDefault(api.KeySyntax, "")) } func prependTitle(title, s0, s1 string) string { if len(title) > 0 { return s1 + title } return s0 |
︙ | ︙ |
Changes to usecase/delete_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/evaluate.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/get_all_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/get_special_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/get_user.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/get_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/lists.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/parse_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/query.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" "errors" |
︙ | ︙ | |||
116 117 118 119 120 121 122 | func (uc *Query) processContextDirective(ctx context.Context, spec *query.ContextSpec, metaSeq []*meta.Meta) []*meta.Meta { return spec.Execute(ctx, metaSeq, uc.port) } func (uc *Query) processItemsDirective(ctx context.Context, _ *query.ItemsSpec, metaSeq []*meta.Meta) []*meta.Meta { result := make([]*meta.Meta, 0, len(metaSeq)) for _, m := range metaSeq { | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | func (uc *Query) processContextDirective(ctx context.Context, spec *query.ContextSpec, metaSeq []*meta.Meta) []*meta.Meta { return spec.Execute(ctx, metaSeq, uc.port) } func (uc *Query) processItemsDirective(ctx context.Context, _ *query.ItemsSpec, metaSeq []*meta.Meta) []*meta.Meta { result := make([]*meta.Meta, 0, len(metaSeq)) for _, m := range metaSeq { zn, err := uc.ucEvaluate.Run(ctx, m.Zid, m.GetDefault(api.KeySyntax, "")) if err != nil { continue } for _, ref := range collect.Order(zn) { if collectedZid, err2 := id.Parse(ref.URL.Path); err2 == nil { if z, err3 := uc.port.GetZettel(ctx, collectedZid); err3 == nil { result = append(result, z.Meta) |
︙ | ︙ | |||
206 207 208 209 210 211 212 | ast.Walk(&v, &is) if v.found { result = append(result, cand) continue candLoop } } | | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | ast.Walk(&v, &is) if v.found { result = append(result, cand) continue candLoop } } syntax := zettel.Meta.GetDefault(api.KeySyntax, "") if !parser.IsASTParser(syntax) { continue } zn := uc.ucEvaluate.RunZettel(ctx, zettel, syntax) ast.Walk(&v, &zn.Ast) if v.found { result = append(result, cand) |
︙ | ︙ |
Changes to usecase/refresh.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/reindex.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ | |||
35 36 37 38 39 40 41 | func NewReIndex(log *logger.Logger, port ReIndexPort) ReIndex { return ReIndex{log: log, port: port} } // Run executes the use case. func (uc *ReIndex) Run(ctx context.Context, zid id.Zid) error { err := uc.port.ReIndex(ctx, zid) | | | 32 33 34 35 36 37 38 39 40 41 | func NewReIndex(log *logger.Logger, port ReIndexPort) ReIndex { return ReIndex{log: log, port: port} } // Run executes the use case. func (uc *ReIndex) Run(ctx context.Context, zid id.Zid) error { err := uc.port.ReIndex(ctx, zid) uc.log.Sense().User(ctx).Err(err).Zid(zid).Msg("ReIndex zettel") return err } |
Changes to usecase/rename_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ |
Changes to usecase/update_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "context" |
︙ | ︙ | |||
69 70 71 72 73 74 75 | } if !hasContent { zettel.Content = oldZettel.Content } zettel.Content.TrimSpace() err = uc.port.UpdateZettel(ctx, zettel) | | | 66 67 68 69 70 71 72 73 74 75 | } if !hasContent { zettel.Content = oldZettel.Content } zettel.Content.TrimSpace() err = uc.port.UpdateZettel(ctx, zettel) uc.log.Sense().User(ctx).Zid(m.Zid).Err(err).Msg("Update zettel") return err } |
Changes to usecase/usecase.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package usecase provides (business) use cases for the zettelstore. package usecase |
Changes to usecase/version.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package usecase import ( "regexp" "strconv" |
︙ | ︙ |
Deleted web/adapter/adapter.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to web/adapter/api/api.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package api provides api handlers for web requests. package api import ( "bytes" |
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 | rtConfig: rtConfig, policy: pol, tokenLifetime: kernel.Main.GetConfig(kernel.WebService, kernel.WebTokenLifetimeAPI).(time.Duration), } return a } // NewURLBuilder creates a new URL builder object with the given key. func (a *API) NewURLBuilder(key byte) *api.URLBuilder { return a.b.NewURLBuilder(key) } func (a *API) getAuthData(ctx context.Context) *server.AuthData { return server.GetAuthData(ctx) } | > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | rtConfig: rtConfig, policy: pol, tokenLifetime: kernel.Main.GetConfig(kernel.WebService, kernel.WebTokenLifetimeAPI).(time.Duration), } return a } // GetURLPrefix returns the configured URL prefix of the web server. func (a *API) GetURLPrefix() string { return a.b.GetURLPrefix() } // NewURLBuilder creates a new URL builder object with the given key. func (a *API) NewURLBuilder(key byte) *api.URLBuilder { return a.b.NewURLBuilder(key) } func (a *API) getAuthData(ctx context.Context) *server.AuthData { return server.GetAuthData(ctx) } |
︙ | ︙ |
Changes to web/adapter/api/command.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "context" "net/http" |
︙ | ︙ |
Changes to web/adapter/api/create_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" |
︙ | ︙ | |||
52 53 54 55 56 57 58 | if err != nil { a.reportUsecaseError(w, err) return } var result []byte var contentType string | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | if err != nil { a.reportUsecaseError(w, err) return } var result []byte var contentType string location := a.NewURLBuilder('z').SetZid(api.ZettelID(newZid.String())) switch enc { case api.EncoderPlain: result = newZid.Bytes() contentType = content.PlainText case api.EncoderData: result = []byte(sx.Int64(newZid).Repr()) contentType = content.SXPF default: panic(encStr) } h := adapter.PrepareHeader(w, contentType) h.Set(api.HeaderLocation, location.String()) w.WriteHeader(http.StatusCreated) if _, err = w.Write(result); err != nil { a.log.Error().Err(err).Zid(newZid).Msg("Create Zettel") } } } |
Changes to web/adapter/api/delete_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/api/get_data.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/api/get_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "bytes" "context" |
︙ | ︙ | |||
26 27 28 29 30 31 32 | "zettelstore.de/z/box" "zettelstore.de/z/encoder" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/content" "zettelstore.de/z/zettel/id" | < | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | "zettelstore.de/z/box" "zettelstore.de/z/encoder" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/content" "zettelstore.de/z/zettel/id" ) // MakeGetZettelHandler creates a new HTTP handler to return a zettel in various encodings. func (a *API) MakeGetZettelHandler(getZettel usecase.GetZettel, parseZettel usecase.ParseZettel, evaluate usecase.Evaluate) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { zid, err := id.Parse(r.URL.Path[1:]) if err != nil { |
︙ | ︙ | |||
95 96 97 98 99 100 101 | } case partMeta: contentType = content.PlainText _, err = z.Meta.Write(&buf) case partContent: | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | } case partMeta: contentType = content.PlainText _, err = z.Meta.Write(&buf) case partContent: contentType = content.MIMEFromSyntax(z.Meta.GetDefault(api.KeySyntax, "")) _, err = z.Content.Write(&buf) } if err != nil { a.log.Fatal().Err(err).Zid(zid).Msg("Unable to store plain zettel/part in buffer") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if err = writeBuffer(w, &buf, contentType); err != nil { a.log.Error().Err(err).Zid(zid).Msg("Write Plain data") } } |
︙ | ︙ | |||
163 164 165 166 167 168 169 | _, err = encdr.WriteZettel(&buf, zn, evalMeta) case partMeta: _, err = encdr.WriteMeta(&buf, zn.InhMeta, evalMeta) case partContent: _, err = encdr.WriteContent(&buf, zn) } if err != nil { | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | _, err = encdr.WriteZettel(&buf, zn, evalMeta) case partMeta: _, err = encdr.WriteMeta(&buf, zn.InhMeta, evalMeta) case partContent: _, err = encdr.WriteContent(&buf, zn) } if err != nil { a.log.Fatal().Err(err).Zid(zn.Zid).Msg("Unable to store data in buffer") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if buf.Len() == 0 { w.WriteHeader(http.StatusNoContent) return } if err = writeBuffer(w, &buf, content.MIMEFromEncoding(enc)); err != nil { a.log.Error().Err(err).Zid(zn.Zid).Msg("Write Encoded Zettel") } } |
Changes to web/adapter/api/login.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" "time" |
︙ | ︙ |
Changes to web/adapter/api/query.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "bytes" "fmt" |
︙ | ︙ | |||
34 35 36 37 38 39 40 | ) // MakeQueryHandler creates a new HTTP handler to perform a query. func (a *API) MakeQueryHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() urlQuery := r.URL.Query() | | > > > > < < < < < < < < < < < < < < < < < < > | | | | | | > > > > > > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | ) // MakeQueryHandler creates a new HTTP handler to perform a query. func (a *API) MakeQueryHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() urlQuery := r.URL.Query() if a.handleTagZettel(w, r, tagZettel, urlQuery) { return } if a.handleRoleZettel(w, r, roleZettel, urlQuery) { return } sq := adapter.GetQuery(urlQuery) metaSeq, err := queryMeta.Run(ctx, sq) if err != nil { a.reportUsecaseError(w, err) return } var encoder zettelEncoder var contentType string switch enc, _ := getEncoding(r, urlQuery); enc { case api.EncoderPlain: encoder = &plainZettelEncoder{} contentType = content.PlainText case api.EncoderData: encoder = &dataZettelEncoder{ sf: sx.MakeMappedFactory(256), sq: sq, getRights: func(m *meta.Meta) api.ZettelRights { return a.getRights(ctx, m) }, } contentType = content.SXPF default: http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } var buf bytes.Buffer err = queryAction(&buf, encoder, metaSeq, sq, func(zid id.Zid) error { return reIndex.Run(ctx, zid) }) if err != nil { a.log.Error().Err(err).Str("query", sq.String()).Msg("execute query action") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } if err = writeBuffer(w, &buf, contentType); err != nil { a.log.Error().Err(err).Msg("write result buffer") } } } func queryAction(w io.Writer, enc zettelEncoder, ml []*meta.Meta, sq *query.Query, reindex func(id.Zid) error) error { min, max := -1, -1 if actions := sq.Actions(); len(actions) > 0 { acts := make([]string, 0, len(actions)) for _, act := range actions { if strings.HasPrefix(act, "MIN") { if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 { min = num continue } } if strings.HasPrefix(act, "MAX") { if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 { max = num continue } } acts = append(acts, act) } for _, act := range acts { switch act { case "KEYS": return encodeKeysArrangement(w, enc, ml, act) case "REINDEX": for _, m := range ml { if err := reindex(m.Zid); err != nil { return err } } } switch key := strings.ToLower(act); meta.Type(key) { case meta.TypeWord, meta.TypeTagSet: return encodeMetaKeyArrangement(w, enc, ml, key, min, max) } } } |
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 | return err } } return nil } type dataZettelEncoder struct { sq *query.Query getRights func(*meta.Meta) api.ZettelRights } func (dze *dataZettelEncoder) writeMetaList(w io.Writer, ml []*meta.Meta) error { | > > | | | | | | > | | | | | > | | > > > | > | < < < < < < | | > | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | return err } } return nil } type dataZettelEncoder struct { sf sx.SymbolFactory sq *query.Query getRights func(*meta.Meta) api.ZettelRights } func (dze *dataZettelEncoder) writeMetaList(w io.Writer, ml []*meta.Meta) error { sf := dze.sf result := make([]sx.Object, len(ml)+1) result[0] = sf.MustMake("list") symID, symZettel := sf.MustMake("id"), sf.MustMake("zettel") for i, m := range ml { msz := sexp.EncodeMetaRights(api.MetaRights{ Meta: m.Map(), Rights: dze.getRights(m), }) msz = sx.Cons(sx.MakeList(symID, sx.Int64(m.Zid)), msz.Cdr()).Cons(symZettel) result[i+1] = msz } _, err := sx.Print(w, sx.MakeList( sf.MustMake("meta-list"), sx.MakeList(sf.MustMake("query"), sx.String(dze.sq.String())), sx.MakeList(sf.MustMake("human"), sx.String(dze.sq.Human())), sx.MakeList(result...), )) return err } func (dze *dataZettelEncoder) writeArrangement(w io.Writer, act string, arr meta.Arrangement) error { sf := dze.sf result := sx.Nil() for aggKey, metaList := range arr { sxMeta := sx.Nil() for i := len(metaList) - 1; i >= 0; i-- { sxMeta = sxMeta.Cons(sx.Int64(metaList[i].Zid)) } sxMeta = sxMeta.Cons(sx.String(aggKey)) result = result.Cons(sxMeta) } _, err := sx.Print(w, sx.MakeList( sf.MustMake("aggregate"), sx.String(act), sx.MakeList(sf.MustMake("query"), sx.String(dze.sq.String())), sx.MakeList(sf.MustMake("human"), sx.String(dze.sq.Human())), result.Cons(sf.MustMake("list")), )) return err } func (a *API) handleTagZettel(w http.ResponseWriter, r *http.Request, tagZettel *usecase.TagZettel, vals url.Values) bool { tag := vals.Get(api.QueryKeyTag) if tag == "" { return false } ctx := r.Context() z, err := tagZettel.Run(ctx, tag) if err != nil { a.reportUsecaseError(w, err) return true } zid := z.Meta.Zid.String() w.Header().Set(api.HeaderContentType, content.PlainText) newURL := a.NewURLBuilder('z').SetZid(api.ZettelID(zid)) for key, slVals := range vals { if key == api.QueryKeyTag { continue } for _, val := range slVals { newURL.AppendKVQuery(key, val) } } http.Redirect(w, r, newURL.String(), http.StatusFound) if _, err = io.WriteString(w, zid); err != nil { a.log.Error().Err(err).Msg("redirect body") } return true } func (a *API) handleRoleZettel(w http.ResponseWriter, r *http.Request, roleZettel *usecase.RoleZettel, vals url.Values) bool { role := vals.Get(api.QueryKeyRole) if role == "" { return false } ctx := r.Context() z, err := roleZettel.Run(ctx, role) if err != nil { a.reportUsecaseError(w, err) return true } zid := z.Meta.Zid.String() w.Header().Set(api.HeaderContentType, content.PlainText) newURL := a.NewURLBuilder('z').SetZid(api.ZettelID(zid)) for key, slVals := range vals { if key == api.QueryKeyRole { continue } for _, val := range slVals { newURL.AppendKVQuery(key, val) } } http.Redirect(w, r, newURL.String(), http.StatusFound) if _, err = io.WriteString(w, zid); err != nil { a.log.Error().Err(err).Msg("redirect body") } return true } |
Changes to web/adapter/api/rename_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" "net/url" |
︙ | ︙ |
Changes to web/adapter/api/request.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "io" "net/http" "net/url" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/sexp" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/input" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // getEncoding returns the data encoding selected by the caller. func getEncoding(r *http.Request, q url.Values) (api.EncodingEnum, string) { |
︙ | ︙ |
Changes to web/adapter/api/response.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "bytes" "net/http" "zettelstore.de/sx.fossil" "zettelstore.de/z/web/content" "zettelstore.de/z/zettel/id" ) func (a *API) writeObject(w http.ResponseWriter, zid id.Zid, obj sx.Object) error { var buf bytes.Buffer if _, err := sx.Print(&buf, obj); err != nil { msg := a.log.Fatal().Err(err) if msg != nil { if zid.IsValid() { msg = msg.Zid(zid) } msg.Msg("Unable to store object in buffer") } http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return nil } return writeBuffer(w, &buf, content.SXPF) } |
Changes to web/adapter/api/update_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package api import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/errors.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package adapter provides handlers for web requests. package adapter import "net/http" // BadRequest signals HTTP status code 400. func BadRequest(w http.ResponseWriter, text string) { http.Error(w, text, http.StatusBadRequest) } // Forbidden signals HTTP status code 403. func Forbidden(w http.ResponseWriter, text string) { http.Error(w, text, http.StatusForbidden) } // NotFound signals HTTP status code 404. func NotFound(w http.ResponseWriter, text string) { http.Error(w, text, http.StatusNotFound) } // ErrResourceNotFound is signalled when a web resource was not found. type ErrResourceNotFound struct{ Path string } func (ernf ErrResourceNotFound) Error() string { return "resource not found: " + ernf.Path } |
Changes to web/adapter/request.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package adapter import ( "net/http" "net/url" |
︙ | ︙ |
Changes to web/adapter/response.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package adapter import ( "errors" "fmt" |
︙ | ︙ |
Changes to web/adapter/webui/const.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package webui // WebUI related constants. const queryKeyAction = "_action" |
︙ | ︙ |
Changes to web/adapter/webui/create_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "bytes" "context" |
︙ | ︙ | |||
143 144 145 146 147 148 149 | newZid, err := createZettel.Run(ctx, zettel) if err != nil { wui.reportError(ctx, w, err) return } if reEdit { | | | < | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | newZid, err := createZettel.Run(ctx, zettel) if err != nil { wui.reportError(ctx, w, err) return } if reEdit { wui.redirectFound(w, r, wui.NewURLBuilder('e').SetZid(api.ZettelID(newZid.String()))) } else { wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(api.ZettelID(newZid.String()))) } } } // MakeGetZettelFromListHandler creates a new HTTP handler to store content of // an existing zettel. func (wui *WebUI) MakeGetZettelFromListHandler( queryMeta *usecase.Query, evaluate *usecase.Evaluate, ucListRoles usecase.ListRoles, ucListSyntax usecase.ListSyntax) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { q := adapter.GetQuery(r.URL.Query()) ctx := r.Context() metaSeq, err := queryMeta.Run(box.NoEnrichQuery(ctx, q), q) if err != nil { wui.reportError(ctx, w, err) return } bns := evaluate.RunBlockNode(ctx, evaluator.QueryAction(ctx, q, metaSeq, wui.rtConfig)) enc := zmkenc.Create() var zmkContent bytes.Buffer _, err = enc.WriteBlocks(&zmkContent, &bns) if err != nil { wui.reportError(ctx, w, err) return } |
︙ | ︙ |
Changes to web/adapter/webui/delete_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/webui/edit_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "net/http" "zettelstore.de/client.fossil/api" "zettelstore.de/z/box" "zettelstore.de/z/usecase" "zettelstore.de/z/web/adapter" "zettelstore.de/z/zettel/id" ) // MakeEditGetZettelHandler creates a new HTTP handler to display the |
︙ | ︙ | |||
70 71 72 73 74 75 76 | } if err = updateZettel.Run(r.Context(), zettel, hasContent); err != nil { wui.reportError(ctx, w, err) return } if reEdit { | | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 | } if err = updateZettel.Run(r.Context(), zettel, hasContent); err != nil { wui.reportError(ctx, w, err) return } if reEdit { wui.redirectFound(w, r, wui.NewURLBuilder('e').SetZid(api.ZettelID(zid.String()))) } else { wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(api.ZettelID(zid.String()))) } } } |
Changes to web/adapter/webui/favicon.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "io" "net/http" "os" "path/filepath" "zettelstore.de/z/web/adapter" ) func (wui *WebUI) MakeFaviconHandler(baseDir string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { filename := filepath.Join(baseDir, "favicon.ico") f, err := os.Open(filename) if err != nil { wui.log.Sense().Err(err).Msg("Favicon not found") http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } defer f.Close() data, err := io.ReadAll(f) if err != nil { wui.log.Info().Err(err).Msg("Unable to read favicon data") http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } if err = adapter.WriteData(w, data, ""); err != nil { wui.log.Error().Err(err).Msg("Write favicon") } } } |
Changes to web/adapter/webui/forms.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "bytes" "errors" "io" "net/http" "regexp" "strings" "unicode" "zettelstore.de/client.fossil/api" "zettelstore.de/z/input" "zettelstore.de/z/kernel" "zettelstore.de/z/parser" "zettelstore.de/z/web/content" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
︙ | ︙ |
Changes to web/adapter/webui/forms_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import "testing" func TestRemoveEmptyLines(t *testing.T) { |
︙ | ︙ |
Changes to web/adapter/webui/get_info.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "net/http" |
︙ | ︙ | |||
82 83 84 85 86 87 88 | } unlinkedMeta, err := ucQuery.Run(ctx, createUnlinkedQuery(zid, phrase)) if err != nil { wui.reportError(ctx, w, err) return } | < | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | } unlinkedMeta, err := ucQuery.Run(ctx, createUnlinkedQuery(zid, phrase)) if err != nil { wui.reportError(ctx, w, err) return } bns := ucEvaluate.RunBlockNode(ctx, evaluator.QueryAction(ctx, nil, unlinkedMeta, wui.rtConfig)) unlinkedContent, _, err := enc.BlocksSxn(&bns) if err != nil { wui.reportError(ctx, w, err) return } encTexts := encodingTexts() shadowLinks := getShadowLinks(ctx, zid, ucGetAllMeta) |
︙ | ︙ | |||
170 171 172 173 174 175 176 | return encTexts } var apiParts = []string{api.PartZettel, api.PartMeta, api.PartContent} func (wui *WebUI) infoAPIMatrix(zid id.Zid, parseOnly bool, encTexts []string) *sx.Pair { matrix := sx.Nil() | | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | return encTexts } var apiParts = []string{api.PartZettel, api.PartMeta, api.PartContent} func (wui *WebUI) infoAPIMatrix(zid id.Zid, parseOnly bool, encTexts []string) *sx.Pair { matrix := sx.Nil() u := wui.NewURLBuilder('z').SetZid(api.ZettelID(zid.String())) for ip := len(apiParts) - 1; ip >= 0; ip-- { part := apiParts[ip] row := sx.Nil() for je := len(encTexts) - 1; je >= 0; je-- { enc := encTexts[je] if parseOnly { u.AppendKVQuery(api.QueryKeyParseOnly, "") } u.AppendKVQuery(api.QueryKeyPart, part) u.AppendKVQuery(api.QueryKeyEncoding, enc) row = row.Cons(sx.Cons(sx.String(enc), sx.String(u.String()))) u.ClearQuery() } matrix = matrix.Cons(sx.Cons(sx.String(part), row)) } return matrix } func (wui *WebUI) infoAPIMatrixParsed(zid id.Zid, encTexts []string) *sx.Pair { matrix := wui.infoAPIMatrix(zid, true, encTexts) u := wui.NewURLBuilder('z').SetZid(api.ZettelID(zid.String())) for i, row := 0, matrix; i < len(apiParts) && row != nil; row = row.Tail() { line, isLine := sx.GetPair(row.Car()) if !isLine || line == nil { continue } last := line.LastPair() |
︙ | ︙ |
Changes to web/adapter/webui/get_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "net/http" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/id" |
︙ | ︙ | |||
57 58 59 60 61 62 63 | } user := server.GetUser(ctx) getTextTitle := wui.makeGetTextTitle(ctx, getZettel) title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle()) env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user) | | < | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | } user := server.GetUser(ctx) getTextTitle := wui.makeGetTextTitle(ctx, getZettel) title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle()) env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user) rb.bindSymbol(wui.symMetaHeader, metaObj) rb.bindString("heading", sx.String(title)) if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" { rb.bindString("role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+role).String())) } if folgeRole, found := zn.InhMeta.Get(api.KeyFolgeRole); found && folgeRole != "" { rb.bindString("folge-role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+folgeRole).String())) } rb.bindString("tag-refs", wui.transformTagSet(api.KeyTags, meta.ListFromValue(zn.InhMeta.GetDefault(api.KeyTags, "")))) rb.bindString("predecessor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPredecessor, getTextTitle)) rb.bindString("precursor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPrecursor, getTextTitle)) rb.bindString("superior-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeySuperior, getTextTitle)) rb.bindString("content", content) rb.bindString("endnotes", endnotes) wui.bindLinks(ctx, &rb, "folge", zn.InhMeta, api.KeyFolge, config.KeyShowFolgeLinks, getTextTitle) wui.bindLinks(ctx, &rb, "subordinate", zn.InhMeta, api.KeySubordinates, config.KeyShowSubordinateLinks, getTextTitle) wui.bindLinks(ctx, &rb, "back", zn.InhMeta, api.KeyBack, config.KeyShowBackLinks, getTextTitle) wui.bindLinks(ctx, &rb, "successor", zn.InhMeta, api.KeySuccessors, config.KeyShowSuccessorLinks, getTextTitle) if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" { |
︙ | ︙ | |||
100 101 102 103 104 105 106 | func (wui *WebUI) identifierSetAsLinks(m *meta.Meta, key string, getTextTitle getTextTitleFunc) *sx.Pair { if values, ok := m.GetList(key); ok { return wui.transformIdentifierSet(values, getTextTitle) } return nil } | < < < < < < < < < < < < | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | func (wui *WebUI) identifierSetAsLinks(m *meta.Meta, key string, getTextTitle getTextTitleFunc) *sx.Pair { if values, ok := m.GetList(key); ok { return wui.transformIdentifierSet(values, getTextTitle) } return nil } func (wui *WebUI) bindLinks(ctx context.Context, rb *renderBinder, varPrefix string, m *meta.Meta, key, configKey string, getTextTitle getTextTitleFunc) { varLinks := varPrefix + "-links" var symOpen *sx.Symbol switch wui.rtConfig.Get(ctx, m, configKey) { case "false": rb.bindString(varLinks, sx.Nil()) return case "close": default: symOpen = wui.symAttrOpen } lstLinks := wui.zettelLinksSxn(m, key, getTextTitle) rb.bindString(varLinks, lstLinks) if sx.IsNil(lstLinks) { return } rb.bindString(varPrefix+"-open", symOpen) |
︙ | ︙ | |||
147 148 149 150 151 152 153 | for i := len(values) - 1; i >= 0; i-- { val := values[i] zid, err := id.Parse(val) if err != nil { continue } if title, found := getTextTitle(zid); found > 0 { | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | for i := len(values) - 1; i >= 0; i-- { val := values[i] zid, err := id.Parse(val) if err != nil { continue } if title, found := getTextTitle(zid); found > 0 { url := sx.String(wui.NewURLBuilder('h').SetZid(api.ZettelID(zid.String())).String()) if title == "" { lst = lst.Cons(sx.Cons(sx.String(val), url)) } else { lst = lst.Cons(sx.Cons(sx.String(title), url)) } } } return lst } |
Changes to web/adapter/webui/goaction.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/webui/home.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "errors" "net/http" "zettelstore.de/client.fossil/api" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" ) |
︙ | ︙ | |||
35 36 37 38 39 40 41 | return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() if p := r.URL.Path; p != "/" { wui.reportError(ctx, w, adapter.ErrResourceNotFound{Path: p}) return } homeZid, _ := id.Parse(wui.rtConfig.Get(ctx, nil, config.KeyHomeZettel)) | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() if p := r.URL.Path; p != "/" { wui.reportError(ctx, w, adapter.ErrResourceNotFound{Path: p}) return } homeZid, _ := id.Parse(wui.rtConfig.Get(ctx, nil, config.KeyHomeZettel)) apiHomeZid := api.ZettelID(homeZid.String()) if homeZid != id.DefaultHomeZid { if _, err := s.GetZettel(ctx, homeZid); err == nil { wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(apiHomeZid)) return } homeZid = id.DefaultHomeZid } |
︙ | ︙ |
Changes to web/adapter/webui/htmlgen.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "net/url" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/attrs" "zettelstore.de/client.fossil/maps" "zettelstore.de/client.fossil/shtml" "zettelstore.de/client.fossil/sz" "zettelstore.de/sx.fossil" "zettelstore.de/z/ast" "zettelstore.de/z/encoder" "zettelstore.de/z/encoder/szenc" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/meta" ) |
︙ | ︙ | |||
41 42 43 44 45 46 47 | tx *szenc.Transformer th *shtml.Evaluator lang string symAt *sx.Symbol } func (wui *WebUI) createGenerator(builder urlBuilder, lang string) *htmlGenerator { | | > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > < | < < < | | | < < < | | < < < < < < < < < < < < < < < < < | | | | | | | | | | | > | | | | | | > | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | tx *szenc.Transformer th *shtml.Evaluator lang string symAt *sx.Symbol } func (wui *WebUI) createGenerator(builder urlBuilder, lang string) *htmlGenerator { th := shtml.NewEvaluator(1, wui.sf) symA := wui.symA symImg := th.Make("img") symAttr := wui.symAttr symHref := wui.symHref symClass := th.Make("class") symTarget := th.Make("target") symRel := th.Make("rel") findA := func(obj sx.Object) (attr, assoc, rest *sx.Pair) { pair, isPair := sx.GetPair(obj) if !isPair || !symA.IsEqual(pair.Car()) { return nil, nil, nil } rest = pair.Tail() if rest == nil { return nil, nil, nil } objA := rest.Car() attr, isPair = sx.GetPair(objA) if !isPair || !symAttr.IsEqual(attr.Car()) { return nil, nil, nil } return attr, attr.Tail(), rest.Tail() } linkZettel := func(obj sx.Object) sx.Object { attr, assoc, rest := findA(obj) if attr == nil { return obj } hrefP := assoc.Assoc(symHref) if hrefP == nil { return obj } href, ok := sx.GetString(hrefP.Cdr()) if !ok { return obj } zid, fragment, hasFragment := strings.Cut(href.String(), "#") u := builder.NewURLBuilder('h').SetZid(api.ZettelID(zid)) if hasFragment { u = u.SetFragment(fragment) } assoc = assoc.Cons(sx.Cons(symHref, sx.String(u.String()))) return rest.Cons(assoc.Cons(symAttr)).Cons(symA) } rebind(th, sz.NameSymLinkZettel, linkZettel) rebind(th, sz.NameSymLinkFound, linkZettel) rebind(th, sz.NameSymLinkBased, func(obj sx.Object) sx.Object { attr, assoc, rest := findA(obj) if attr == nil { return obj } hrefP := assoc.Assoc(symHref) if hrefP == nil { return obj } href, ok := sx.GetString(hrefP.Cdr()) if !ok { return obj } u := builder.NewURLBuilder('/').SetRawLocal(href.String()) assoc = assoc.Cons(sx.Cons(symHref, sx.String(u.String()))) return rest.Cons(assoc.Cons(symAttr)).Cons(symA) }) rebind(th, sz.NameSymLinkQuery, func(obj sx.Object) sx.Object { attr, assoc, rest := findA(obj) if attr == nil { return obj } hrefP := assoc.Assoc(symHref) if hrefP == nil { return obj } href, ok := sx.GetString(hrefP.Cdr()) if !ok { return obj } ur, err := url.Parse(href.String()) if err != nil { return obj } q := ur.Query().Get(api.QueryKeyQuery) if q == "" { return obj } u := builder.NewURLBuilder('h').AppendQuery(q) assoc = assoc.Cons(sx.Cons(symHref, sx.String(u.String()))) return rest.Cons(assoc.Cons(symAttr)).Cons(symA) }) rebind(th, sz.NameSymLinkExternal, func(obj sx.Object) sx.Object { attr, assoc, rest := findA(obj) if attr == nil { return obj } assoc = assoc.Cons(sx.Cons(symClass, sx.String("external"))). Cons(sx.Cons(symTarget, sx.String("_blank"))). Cons(sx.Cons(symRel, sx.String("noopener noreferrer"))) return rest.Cons(assoc.Cons(symAttr)).Cons(symA) }) rebind(th, sz.NameSymEmbed, func(obj sx.Object) sx.Object { pair, isPair := sx.GetPair(obj) if !isPair || !symImg.IsEqual(pair.Car()) { return obj } attr, isPair := sx.GetPair(pair.Tail().Car()) if !isPair || !symAttr.IsEqual(attr.Car()) { return obj } symSrc := th.Make("src") srcP := attr.Tail().Assoc(symSrc) if srcP == nil { return obj } src, isString := sx.GetString(srcP.Cdr()) if !isString { return obj } zid := api.ZettelID(src) if !zid.IsValid() { return obj } u := builder.NewURLBuilder('z').SetZid(zid) imgAttr := attr.Tail().Cons(sx.Cons(symSrc, sx.String(u.String()))).Cons(symAttr) return pair.Tail().Tail().Cons(imgAttr).Cons(symImg) }) return &htmlGenerator{ tx: szenc.NewTransformer(), th: th, lang: lang, symAt: symAttr, } } func rebind(ev *shtml.Evaluator, name string, fn func(sx.Object) sx.Object) { prevFn := ev.ResolveBinding(name) ev.Rebind(name, func(args []sx.Object, env *shtml.Environment) sx.Object { obj := prevFn(args, env) if env.GetError() == nil { return fn(obj) } return sx.Nil() }) } |
︙ | ︙ | |||
223 224 225 226 227 228 229 | for aelem := att.Tail(); aelem != nil; aelem = aelem.Tail() { if p, ok := sx.GetPair(aelem.Car()); ok { key := p.Car() val := p.Cdr() if tail, isTail := sx.GetPair(val); isTail { val = tail.Car() } | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | for aelem := att.Tail(); aelem != nil; aelem = aelem.Tail() { if p, ok := sx.GetPair(aelem.Car()); ok { key := p.Car() val := p.Cdr() if tail, isTail := sx.GetPair(val); isTail { val = tail.Car() } a = a.Set(key.String(), val.String()) } } name, found := a.Get("name") if !found || ignore.Has(name) { continue } |
︙ | ︙ |
Changes to web/adapter/webui/htmlmeta.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "errors" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/z/ast" "zettelstore.de/z/box" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/zettel/id" |
︙ | ︙ | |||
41 42 43 44 45 46 47 | case meta.TypeEmpty: return sx.String(value) case meta.TypeID: return wui.transformIdentifier(value, getTextTitle) case meta.TypeIDSet: return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle) case meta.TypeNumber: | | | | | | | > > | | | | | | | | | | | | < < < | | < > | | | < | | < > | | | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | case meta.TypeEmpty: return sx.String(value) case meta.TypeID: return wui.transformIdentifier(value, getTextTitle) case meta.TypeIDSet: return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle) case meta.TypeNumber: return wui.transformLink(key, value, value) case meta.TypeString: return sx.String(value) case meta.TypeTagSet: return wui.transformTagSet(key, meta.ListFromValue(value)) case meta.TypeTimestamp: if ts, ok := meta.TimeValue(value); ok { return sx.MakeList( wui.sf.MustMake("time"), sx.MakeList( wui.symAttr, sx.Cons(wui.sf.MustMake("datetime"), sx.String(ts.Format("2006-01-02T15:04:05"))), ), sx.MakeList(wui.sf.MustMake(sxhtml.NameSymNoEscape), sx.String(ts.Format("2006-01-02 15:04:05"))), ) } return sx.Nil() case meta.TypeURL: return wui.url2html(sx.String(value)) case meta.TypeWord: return wui.transformLink(key, value, value) case meta.TypeWordSet: return wui.transformWordSet(key, meta.ListFromValue(value)) case meta.TypeZettelmarkup: return wui.transformZmkMetadata(value, evalMetadata, gen) default: return sx.MakeList(wui.sf.MustMake("b"), sx.String("Unhandled type: "), sx.String(kt.Name)) } } func (wui *WebUI) transformIdentifier(val string, getTextTitle getTextTitleFunc) sx.Object { text := sx.String(val) zid, err := id.Parse(val) if err != nil { return text } title, found := getTextTitle(zid) switch { case found > 0: ub := wui.NewURLBuilder('h').SetZid(api.ZettelID(zid.String())) attrs := sx.Nil() if title != "" { attrs = attrs.Cons(sx.Cons(wui.sf.MustMake("title"), sx.String(title))) } attrs = attrs.Cons(sx.Cons(wui.symHref, sx.String(ub.String()))).Cons(wui.symAttr) return sx.Nil().Cons(sx.String(zid.String())).Cons(attrs).Cons(wui.symA) case found == 0: return sx.MakeList(wui.sf.MustMake("s"), text) default: // case found < 0: return text } } func (wui *WebUI) transformIdentifierSet(vals []string, getTextTitle getTextTitleFunc) *sx.Pair { if len(vals) == 0 { return nil } space := sx.String(" ") text := make([]sx.Object, 0, 2*len(vals)) for _, val := range vals { text = append(text, space, wui.transformIdentifier(val, getTextTitle)) } return sx.MakeList(text[1:]...).Cons(wui.symSpan) } func (wui *WebUI) transformTagSet(key string, tags []string) *sx.Pair { if len(tags) == 0 { return nil } space := sx.String(" ") text := make([]sx.Object, 0, 2*len(tags)) for _, tag := range tags { text = append(text, space, wui.transformLink(key, tag, tag)) } return sx.MakeList(text[1:]...).Cons(wui.symSpan) } func (wui *WebUI) transformWordSet(key string, words []string) sx.Object { if len(words) == 0 { return sx.Nil() } space := sx.String(" ") text := make([]sx.Object, 0, 2*len(words)) for _, word := range words { text = append(text, space, wui.transformLink(key, word, word)) } return sx.MakeList(text[1:]...).Cons(wui.symSpan) } func (wui *WebUI) transformLink(key, value, text string) *sx.Pair { return sx.MakeList( wui.symA, sx.MakeList( wui.symAttr, sx.Cons(wui.symHref, sx.String(wui.NewURLBuilder('h').AppendQuery(key+api.SearchOperatorHas+value).String())), ), sx.String(text), ) } type evalMetadataFunc = func(string) ast.InlineSlice |
︙ | ︙ | |||
166 167 168 169 170 171 172 | } return parser.NormalizedSpacedText(z.Meta.GetTitle()), 1 } } func (wui *WebUI) transformZmkMetadata(value string, evalMetadata evalMetadataFunc, gen *htmlGenerator) sx.Object { is := evalMetadata(value) | | | 160 161 162 163 164 165 166 167 168 | } return parser.NormalizedSpacedText(z.Meta.GetTitle()), 1 } } func (wui *WebUI) transformZmkMetadata(value string, evalMetadata evalMetadataFunc, gen *htmlGenerator) sx.Object { is := evalMetadata(value) return gen.InlinesSxHTML(&is).Cons(wui.symSpan) } |
Changes to web/adapter/webui/lists.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < < | > > | | > > > > | | | | < < < < < < < > > > > | | | | | | | | | | < | < | | < < < | | | | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "io" "net/http" "net/url" "slices" "strconv" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/z/ast" "zettelstore.de/z/encoding/atom" "zettelstore.de/z/encoding/rss" "zettelstore.de/z/encoding/xml" "zettelstore.de/z/evaluator" "zettelstore.de/z/query" "zettelstore.de/z/usecase" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // MakeListHTMLMetaHandler creates a HTTP handler for rendering the list of zettel as HTML. func (wui *WebUI) MakeListHTMLMetaHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { urlQuery := r.URL.Query() if wui.handleTagZettel(w, r, tagZettel, urlQuery) { return } if wui.handleRoleZettel(w, r, roleZettel, urlQuery) { return } q := adapter.GetQuery(urlQuery) q = q.SetDeterministic() ctx := r.Context() metaSeq, err := queryMeta.Run(ctx, q) if err != nil { wui.reportError(ctx, w, err) return } if actions := q.Actions(); len(actions) > 0 { var tempActions []string for _, act := range actions { if act == "REINDEX" { for _, m := range metaSeq { if err = reIndex.Run(ctx, m.Zid); err != nil { wui.reportError(ctx, w, err) return } } continue } tempActions = append(tempActions, act) } actions = tempActions if len(actions) > 0 { switch actions[0] { case "ATOM": wui.renderAtom(w, q, metaSeq) return case "RSS": wui.renderRSS(ctx, w, q, metaSeq) return } } } var content, endnotes *sx.Pair if bn := evaluator.QueryAction(ctx, q, metaSeq, wui.rtConfig); bn != nil { enc := wui.getSimpleHTMLEncoder(wui.rtConfig.Get(ctx, nil, api.KeyLang)) content, endnotes, err = enc.BlocksSxn(&ast.BlockSlice{bn}) if err != nil { wui.reportError(ctx, w, err) return } } user := server.GetUser(ctx) env, rb := wui.createRenderEnv( ctx, "list", wui.rtConfig.Get(ctx, nil, api.KeyLang), wui.rtConfig.GetSiteName(), user) if q == nil { rb.bindString("heading", sx.String(wui.rtConfig.GetSiteName())) } else { var sb strings.Builder q.PrintHuman(&sb) rb.bindString("heading", sx.String(sb.String())) } rb.bindString("query-value", sx.String(q.String())) if tzl := q.GetMetaValues(api.KeyTags); len(tzl) > 0 { sxTzl, sxNoTzl := wui.transformTagZettelList(ctx, tagZettel, tzl) if !sx.IsNil(sxTzl) { rb.bindString("tag-zettel", sxTzl) } if !sx.IsNil(sxNoTzl) && wui.canCreate(ctx, user) { rb.bindString("create-tag-zettel", sxNoTzl) } } if rzl := q.GetMetaValues(api.KeyRole); len(rzl) > 0 { sxRzl, sxNoRzl := wui.transformRoleZettelList(ctx, roleZettel, rzl) if !sx.IsNil(sxRzl) { rb.bindString("role-zettel", sxRzl) } if !sx.IsNil(sxNoRzl) && wui.canCreate(ctx, user) { rb.bindString("create-role-zettel", sxNoRzl) } } rb.bindString("content", content) rb.bindString("endnotes", endnotes) apiURL := wui.NewURLBuilder('z').AppendQuery(q.String()) seed, found := q.GetSeed() if found { apiURL = apiURL.AppendKVQuery(api.QueryKeySeed, strconv.Itoa(seed)) } else { seed = 0 } rb.bindString("plain-url", sx.String(apiURL.String())) rb.bindString("data-url", sx.String(apiURL.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData).String())) if wui.canCreate(ctx, user) { rb.bindString("create-url", sx.String(wui.createNewURL)) rb.bindString("seed", sx.Int64(seed)) } if rb.err == nil { err = wui.renderSxnTemplate(ctx, w, id.ListTemplateZid, env) } else { err = rb.err } if err != nil { |
︙ | ︙ | |||
180 181 182 183 184 185 186 | } } return withZettel, withoutZettel } func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair { link := sx.MakeList( | | | | | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | } } return withZettel, withoutZettel } func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair { link := sx.MakeList( wui.symA, sx.MakeList( wui.symAttr, sx.Cons(wui.symHref, sx.String(u.String())), ), sx.String(name), ) if sxZtl != nil { sxZtl = sxZtl.Cons(sx.String(", ")) } return sxZtl.Cons(link) } func (wui *WebUI) renderRSS(ctx context.Context, w http.ResponseWriter, q *query.Query, ml []*meta.Meta) { var rssConfig rss.Configuration rssConfig.Setup(ctx, wui.rtConfig) if actions := q.Actions(); len(actions) > 2 && actions[1] == "TITLE" { rssConfig.Title = strings.Join(actions[2:], " ") } data := rssConfig.Marshal(q, ml) adapter.PrepareHeader(w, rss.ContentType) w.WriteHeader(http.StatusOK) var err error if _, err = io.WriteString(w, xml.Header); err == nil { _, err = w.Write(data) } if err != nil { wui.log.Error().Err(err).Msg("unable to write RSS data") } } func (wui *WebUI) renderAtom(w http.ResponseWriter, q *query.Query, ml []*meta.Meta) { var atomConfig atom.Configuration atomConfig.Setup(wui.rtConfig) if actions := q.Actions(); len(actions) > 2 && actions[1] == "TITLE" { atomConfig.Title = strings.Join(actions[2:], " ") } data := atomConfig.Marshal(q, ml) adapter.PrepareHeader(w, atom.ContentType) w.WriteHeader(http.StatusOK) var err error |
︙ | ︙ | |||
242 243 244 245 246 247 248 | } ctx := r.Context() z, err := tagZettel.Run(ctx, tag) if err != nil { wui.reportError(ctx, w, err) return true } | | | | 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | } ctx := r.Context() z, err := tagZettel.Run(ctx, tag) if err != nil { wui.reportError(ctx, w, err) return true } wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(api.ZettelID(z.Meta.Zid.String()))) return true } func (wui *WebUI) handleRoleZettel(w http.ResponseWriter, r *http.Request, roleZettel *usecase.RoleZettel, vals url.Values) bool { role := vals.Get(api.QueryKeyRole) if role == "" { return false } ctx := r.Context() z, err := roleZettel.Run(ctx, role) if err != nil { wui.reportError(ctx, w, err) return true } wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(api.ZettelID(z.Meta.Zid.String()))) return true } |
Changes to web/adapter/webui/login.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "net/http" |
︙ | ︙ |
Deleted web/adapter/webui/meta.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted web/adapter/webui/meta_test.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to web/adapter/webui/rename_zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "fmt" "net/http" |
︙ | ︙ | |||
96 97 98 99 100 101 102 | return } if err = renameZettel.Run(r.Context(), curZid, newZid); err != nil { wui.reportError(ctx, w, err) return } | | | 93 94 95 96 97 98 99 100 101 102 | return } if err = renameZettel.Run(r.Context(), curZid, newZid); err != nil { wui.reportError(ctx, w, err) return } wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(api.ZettelID(newZid.String()))) } } |
Changes to web/adapter/webui/response.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "net/http" |
︙ | ︙ |
Changes to web/adapter/webui/sxn_code.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "context" "fmt" "io" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil/sxeval" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) func (wui *WebUI) loadAllSxnCodeZettel(ctx context.Context) (id.Digraph, sxeval.Environment, error) { // getMeta MUST currently use GetZettel, because GetMeta just uses the // Index, which might not be current. getMeta := func(ctx context.Context, zid id.Zid) (*meta.Meta, error) { z, err := wui.box.GetZettel(ctx, zid) if err != nil { return nil, err } return z.Meta, nil } dg := buildSxnCodeDigraph(ctx, id.StartSxnZid, getMeta) if dg == nil { return nil, wui.engine.RootEnvironment(), nil } dg = dg.AddVertex(id.BaseSxnZid).AddEdge(id.StartSxnZid, id.BaseSxnZid) dg = dg.AddVertex(id.PreludeSxnZid).AddEdge(id.BaseSxnZid, id.PreludeSxnZid) dg = dg.TransitiveClosure(id.StartSxnZid) if zid, isDAG := dg.IsDAG(); !isDAG { return nil, nil, fmt.Errorf("zettel %v is part of a dependency cycle", zid) } env := sxeval.MakeChildEnvironment(wui.engine.RootEnvironment(), "zettel", 128) for _, zid := range dg.SortReverse() { if err := wui.loadSxnCodeZettel(ctx, zid, env); err != nil { return nil, nil, err } } return dg, env, nil } type getMetaFunc func(context.Context, id.Zid) (*meta.Meta, error) func buildSxnCodeDigraph(ctx context.Context, startZid id.Zid, getMeta getMetaFunc) id.Digraph { m, err := getMeta(ctx, startZid) if err != nil { |
︙ | ︙ | |||
84 85 86 87 88 89 90 | } } } } return dg } | | < | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | } } } } return dg } func (wui *WebUI) loadSxnCodeZettel(ctx context.Context, zid id.Zid, env sxeval.Environment) error { rdr, err := wui.makeZettelReader(ctx, zid) if err != nil { return err } for { form, err2 := rdr.Read() if err2 != nil { if err2 == io.EOF { return nil } return err2 } wui.log.Debug().Zid(zid).Str("form", form.Repr()).Msg("Loaded sxn code") if _, err2 = wui.engine.Eval(form, env, nil); err2 != nil { return err2 } } } |
Changes to web/adapter/webui/template.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < < | | > | | | | | | | | | | | | | > | < | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package webui import ( "bytes" "context" "fmt" "net/http" "net/url" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxbuiltins" "zettelstore.de/sx.fossil/sxeval" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/box" "zettelstore.de/z/collect" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) func (wui *WebUI) createRenderEngine() *sxeval.Engine { root := sxeval.MakeRootEnvironment(len(specials) + len(builtins) + 3) engine := sxeval.MakeEngine(wui.sf, root) for _, syntax := range specials { engine.BindSpecial(syntax) } for _, b := range builtins { engine.BindBuiltin(b) } engine.BindBuiltin(&sxeval.Builtin{ Name: "url-to-html", MinArity: 1, MaxArity: 1, TestPure: sxeval.AssertPure, Fn: func(_ *sxeval.Frame, args []sx.Object) (sx.Object, error) { text, err := sxbuiltins.GetString(args, 0) if err != nil { return nil, err } return wui.url2html(text), nil }, }) engine.BindBuiltin(&sxeval.Builtin{ Name: "zid-content-path", MinArity: 1, MaxArity: 1, TestPure: sxeval.AssertPure, Fn: func(_ *sxeval.Frame, args []sx.Object) (sx.Object, error) { s, err := sxbuiltins.GetString(args, 0) if err != nil { return nil, err } zid, err := id.Parse(s.String()) if err != nil { return nil, fmt.Errorf("parsing zettel identfier %q: %w", s, err) } ub := wui.NewURLBuilder('z').SetZid(api.ZettelID(zid.String())) return sx.String(ub.String()), nil }, }) engine.BindBuiltin(&sxeval.Builtin{ Name: "query->url", MinArity: 1, MaxArity: 1, TestPure: sxeval.AssertPure, Fn: func(_ *sxeval.Frame, args []sx.Object) (sx.Object, error) { qs, err := sxbuiltins.GetString(args, 0) if err != nil { return nil, err } u := wui.NewURLBuilder('h').AppendQuery(qs.String()) return sx.String(u.String()), nil }, }) root.Freeze() return engine } var ( specials = []*sxeval.Special{ &sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote &sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing &sxbuiltins.DefVarS, &sxbuiltins.DefConstS, // defvar, defconst &sxbuiltins.DefunS, &sxbuiltins.LambdaS, // defun, lambda &sxbuiltins.SetXS, // set! &sxbuiltins.CondS, // cond &sxbuiltins.IfS, // if &sxbuiltins.BeginS, // begin &sxbuiltins.DefMacroS, // defmacro } builtins = []*sxeval.Builtin{ &sxbuiltins.Identical, // == &sxbuiltins.NullP, // null? &sxbuiltins.PairP, // pair? &sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr &sxbuiltins.Caar, &sxbuiltins.Cadr, &sxbuiltins.Cdar, &sxbuiltins.Cddr, &sxbuiltins.Caaar, &sxbuiltins.Caadr, &sxbuiltins.Cadar, &sxbuiltins.Caddr, &sxbuiltins.Cdaar, &sxbuiltins.Cdadr, &sxbuiltins.Cddar, &sxbuiltins.Cdddr, &sxbuiltins.List, // list &sxbuiltins.Append, // append &sxbuiltins.Assoc, // assoc &sxbuiltins.Map, // map &sxbuiltins.Apply, // apply &sxbuiltins.StringAppend, // string-append &sxbuiltins.BoundP, // bound? &sxbuiltins.Defined, // defined? &sxbuiltins.CurrentEnv, // current-environment &sxbuiltins.EnvLookup, // environment-lookup } ) func (wui *WebUI) url2html(text sx.String) sx.Object { if u, errURL := url.Parse(text.String()); errURL == nil { if us := u.String(); us != "" { return sx.MakeList( wui.symA, sx.MakeList( wui.symAttr, sx.Cons(wui.symHref, sx.String(us)), sx.Cons(wui.sf.MustMake("target"), sx.String("_blank")), sx.Cons(wui.sf.MustMake("rel"), sx.String("noopener noreferrer")), ), text) } } return text } func (wui *WebUI) getParentEnv(ctx context.Context) (sxeval.Environment, error) { wui.mxZettelEnv.Lock() defer wui.mxZettelEnv.Unlock() if parentEnv := wui.zettelEnv; parentEnv != nil { return parentEnv, nil } dag, zettelEnv, err := wui.loadAllSxnCodeZettel(ctx) if err != nil { wui.log.Error().Err(err).Msg("loading zettel sxn") return nil, err } wui.dag = dag wui.zettelEnv = zettelEnv return zettelEnv, nil } // createRenderEnv creates a new environment and populates it with all relevant data for the base template. func (wui *WebUI) createRenderEnv(ctx context.Context, name, lang, title string, user *meta.Meta) (sxeval.Environment, renderBinder) { userIsValid, userZettelURL, userIdent := wui.getUserRenderData(user) parentEnv, err := wui.getParentEnv(ctx) env := sxeval.MakeChildEnvironment(parentEnv, name, 128) rb := makeRenderBinder(wui.sf, env, err) rb.bindString("lang", sx.String(lang)) rb.bindString("css-base-url", sx.String(wui.cssBaseURL)) rb.bindString("css-user-url", sx.String(wui.cssUserURL)) rb.bindString("title", sx.String(title)) rb.bindString("home-url", sx.String(wui.homeURL)) rb.bindString("with-auth", sx.MakeBoolean(wui.withAuth)) rb.bindString("user-is-valid", sx.MakeBoolean(userIsValid)) |
︙ | ︙ | |||
186 187 188 189 190 191 192 | } rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user)) rb.bindString("search-url", sx.String(wui.searchURL)) rb.bindString("query-key-query", sx.String(api.QueryKeyQuery)) rb.bindString("query-key-seed", sx.String(api.QueryKeySeed)) rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer rb.bindString("debug-mode", sx.MakeBoolean(wui.debug)) | | | | | | > | | | > > > > > | | > > | | > | > > > | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | } rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user)) rb.bindString("search-url", sx.String(wui.searchURL)) rb.bindString("query-key-query", sx.String(api.QueryKeyQuery)) rb.bindString("query-key-seed", sx.String(api.QueryKeySeed)) rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer rb.bindString("debug-mode", sx.MakeBoolean(wui.debug)) rb.bindSymbol(wui.symMetaHeader, sx.Nil()) rb.bindSymbol(wui.symDetail, sx.Nil()) return env, rb } func (wui *WebUI) getUserRenderData(user *meta.Meta) (bool, string, string) { if user == nil { return false, "", "" } return true, wui.NewURLBuilder('h').SetZid(api.ZettelID(user.Zid.String())).String(), user.GetDefault(api.KeyUserID, "") } type renderBinder struct { err error make func(string) (*sx.Symbol, error) env sxeval.Environment } func makeRenderBinder(sf sx.SymbolFactory, env sxeval.Environment, err error) renderBinder { return renderBinder{make: sf.Make, env: env, err: err} } func (rb *renderBinder) bindString(key string, obj sx.Object) { if rb.err == nil { sym, err := rb.make(key) if err == nil { rb.err = rb.env.Bind(sym, obj) return } rb.err = err } } func (rb *renderBinder) bindSymbol(sym *sx.Symbol, obj sx.Object) { if rb.err == nil { rb.err = rb.env.Bind(sym, obj) } } func (rb *renderBinder) bindKeyValue(key string, value string) { rb.bindString("meta-"+key, sx.String(value)) if kt := meta.Type(key); kt.IsSet { rb.bindString("set-meta-"+key, makeStringList(meta.ListFromValue(value))) } } func (rb *renderBinder) rebindResolved(key, defKey string) { if rb.err == nil { sym, err := rb.make(key) if err == nil { if obj, found := sxeval.Resolve(rb.env, sym); found { rb.bindString(defKey, obj) return } return } rb.err = err } } func (wui *WebUI) bindCommonZettelData(ctx context.Context, rb *renderBinder, user, m *meta.Meta, content *zettel.Content) { strZid := m.Zid.String() apiZid := api.ZettelID(strZid) newURLBuilder := wui.NewURLBuilder |
︙ | ︙ | |||
258 259 260 261 262 263 264 | } if wui.canDelete(ctx, user, m) { rb.bindString("delete-url", sx.String(newURLBuilder('d').SetZid(apiZid).String())) } if val, found := m.Get(api.KeyUselessFiles); found { rb.bindString("useless", sx.Cons(sx.String(val), nil)) } | < | < < | < | | > | > | | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | } if wui.canDelete(ctx, user, m) { rb.bindString("delete-url", sx.String(newURLBuilder('d').SetZid(apiZid).String())) } if val, found := m.Get(api.KeyUselessFiles); found { rb.bindString("useless", sx.Cons(sx.String(val), nil)) } rb.bindString("context-url", sx.String(newURLBuilder('h').AppendQuery(strZid+" "+api.ContextDirective).String())) if wui.canRefresh(user) { rb.bindString("reindex-url", sx.String(newURLBuilder('h').AppendQuery(strZid+" "+api.IdentDirective+api.ActionSeparator+"REINDEX").String())) } // Ensure to have title, role, tags, and syntax included as "meta-*" rb.bindKeyValue(api.KeyTitle, m.GetDefault(api.KeyTitle, "")) rb.bindKeyValue(api.KeyRole, m.GetDefault(api.KeyRole, "")) rb.bindKeyValue(api.KeyTags, m.GetDefault(api.KeyTags, "")) rb.bindKeyValue(api.KeySyntax, m.GetDefault(api.KeySyntax, "")) sentinel := sx.Cons(nil, nil) curr := sentinel for _, p := range m.ComputedPairs() { key, value := p.Key, p.Value curr = curr.AppendBang(sx.Cons(sx.String(key), sx.String(value))) rb.bindKeyValue(key, value) } rb.bindString("metapairs", sentinel.Tail()) } func (wui *WebUI) fetchNewTemplatesSxn(ctx context.Context, user *meta.Meta) (lst *sx.Pair) { if !wui.canCreate(ctx, user) { return nil } ctx = box.NoEnrichContext(ctx) |
︙ | ︙ | |||
304 305 306 307 308 309 310 | if err2 != nil { continue } if !wui.policy.CanRead(user, z.Meta) { continue } text := sx.String(parser.NormalizedSpacedText(z.Meta.GetTitle())) | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | if err2 != nil { continue } if !wui.policy.CanRead(user, z.Meta) { continue } text := sx.String(parser.NormalizedSpacedText(z.Meta.GetTitle())) link := sx.String(wui.NewURLBuilder('c').SetZid(api.ZettelID(zid.String())). AppendKVQuery(queryKeyAction, valueActionNew).String()) lst = lst.Cons(sx.Cons(text, link)) } return lst } func (wui *WebUI) calculateFooterSxn(ctx context.Context) *sx.Pair { |
︙ | ︙ | |||
326 327 328 329 330 331 332 | return content } } } return nil } | | < | | | | | < | | | | | | | < < | | < | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | return content } } } return nil } func (wui *WebUI) getSxnTemplate(ctx context.Context, zid id.Zid, env sxeval.Environment) (sxeval.Expr, error) { if t := wui.getSxnCache(zid); t != nil { return t, nil } reader, err := wui.makeZettelReader(ctx, zid) if err != nil { return nil, err } objs, err := reader.ReadAll() if err != nil { wui.log.Error().Err(err).Zid(zid).Msg("reading sxn template") return nil, err } if len(objs) != 1 { return nil, fmt.Errorf("expected 1 expression in template, but got %d", len(objs)) } t, err := wui.engine.Parse(objs[0], env) if err != nil { return nil, err } wui.setSxnCache(zid, wui.engine.Rework(t, env)) return t, nil } func (wui *WebUI) makeZettelReader(ctx context.Context, zid id.Zid) (*sxreader.Reader, error) { ztl, err := wui.box.GetZettel(ctx, zid) if err != nil { return nil, err } reader := sxreader.MakeReader(bytes.NewReader(ztl.Content.AsBytes()), sxreader.WithSymbolFactory(wui.sf)) return reader, nil } func (wui *WebUI) evalSxnTemplate(ctx context.Context, zid id.Zid, env sxeval.Environment) (sx.Object, error) { templateExpr, err := wui.getSxnTemplate(ctx, zid, env) if err != nil { return nil, err } return wui.engine.Execute(templateExpr, env, nil) } func (wui *WebUI) renderSxnTemplate(ctx context.Context, w http.ResponseWriter, templateID id.Zid, env sxeval.Environment) error { return wui.renderSxnTemplateStatus(ctx, w, http.StatusOK, templateID, env) } func (wui *WebUI) renderSxnTemplateStatus(ctx context.Context, w http.ResponseWriter, code int, templateID id.Zid, env sxeval.Environment) error { detailObj, err := wui.evalSxnTemplate(ctx, templateID, env) if err != nil { return err } env.Bind(wui.symDetail, detailObj) pageObj, err := wui.evalSxnTemplate(ctx, id.BaseTemplateZid, env) if err != nil { return err } wui.log.Debug().Str("page", pageObj.Repr()).Msg("render") gen := sxhtml.NewGenerator(wui.sf, sxhtml.WithNewline) var sb bytes.Buffer _, err = gen.WriteHTML(&sb, pageObj) if err != nil { return err } wui.prepareAndWriteHeader(w, code) if _, err = w.Write(sb.Bytes()); err != nil { |
︙ | ︙ |
Changes to web/adapter/webui/webui.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package webui provides web-UI handlers for web requests. package webui import ( "context" |
︙ | ︙ | |||
63 64 65 66 67 68 69 | refreshURL string withAuth bool loginURL string logoutURL string searchURL string createNewURL string | > | | | | | > > > > > > > > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | refreshURL string withAuth bool loginURL string logoutURL string searchURL string createNewURL string sf sx.SymbolFactory engine *sxeval.Engine mxZettelEnv sync.Mutex zettelEnv sxeval.Environment dag id.Digraph genHTML *sxhtml.Generator symMetaHeader *sx.Symbol symDetail *sx.Symbol symA, symHref *sx.Symbol symSpan *sx.Symbol symAttr *sx.Symbol symAttrOpen *sx.Symbol } // webuiBox contains all box methods that are needed for WebUI operation. // // Note: these function must not do auth checking. type webuiBox interface { CanCreateZettel(context.Context) bool GetZettel(context.Context, id.Zid) (zettel.Zettel, error) GetMeta(context.Context, id.Zid) (*meta.Meta, error) CanUpdateZettel(context.Context, zettel.Zettel) bool AllowRenameZettel(context.Context, id.Zid) bool CanDeleteZettel(context.Context, id.Zid) bool } // New creates a new WebUI struct. func New(log *logger.Logger, ab server.AuthBuilder, authz auth.AuthzManager, rtConfig config.Config, token auth.TokenManager, mgr box.Manager, pol auth.Policy, evalZettel *usecase.Evaluate) *WebUI { loginoutBase := ab.NewURLBuilder('i') sf := sx.MakeMappedFactory(256) wui := &WebUI{ log: log, debug: kernel.Main.GetConfig(kernel.CoreService, kernel.CoreDebug).(bool), ab: ab, rtConfig: rtConfig, authz: authz, |
︙ | ︙ | |||
115 116 117 118 119 120 121 | refreshURL: ab.NewURLBuilder('g').AppendKVQuery("_c", "r").String(), withAuth: authz.WithAuth(), loginURL: loginoutBase.String(), logoutURL: loginoutBase.AppendKVQuery("logout", "").String(), searchURL: ab.NewURLBuilder('h').String(), createNewURL: ab.NewURLBuilder('c').String(), | > | | > > > > > > > | < < < < < | | | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | refreshURL: ab.NewURLBuilder('g').AppendKVQuery("_c", "r").String(), withAuth: authz.WithAuth(), loginURL: loginoutBase.String(), logoutURL: loginoutBase.AppendKVQuery("logout", "").String(), searchURL: ab.NewURLBuilder('h').String(), createNewURL: ab.NewURLBuilder('c').String(), sf: sf, zettelEnv: nil, genHTML: sxhtml.NewGenerator(sf, sxhtml.WithNewline), symDetail: sf.MustMake("DETAIL"), symMetaHeader: sf.MustMake("META-HEADER"), symA: sf.MustMake("a"), symHref: sf.MustMake("href"), symSpan: sf.MustMake("span"), symAttr: sf.MustMake(sxhtml.NameSymAttr), symAttrOpen: sf.MustMake("open"), } wui.engine = wui.createRenderEngine() wui.observe(box.UpdateInfo{Box: mgr, Reason: box.OnReload, Zid: id.Invalid}) mgr.RegisterObserver(wui.observe) return wui } func (wui *WebUI) observe(ci box.UpdateInfo) { wui.mxCache.Lock() if ci.Reason == box.OnReload { clear(wui.templateCache) } else { delete(wui.templateCache, ci.Zid) } wui.mxCache.Unlock() wui.mxZettelEnv.Lock() if ci.Reason == box.OnReload || wui.dag.HasVertex(ci.Zid) { wui.zettelEnv = nil wui.dag = nil } wui.mxZettelEnv.Unlock() } func (wui *WebUI) setSxnCache(zid id.Zid, expr sxeval.Expr) { wui.mxCache.Lock() wui.templateCache[zid] = expr wui.mxCache.Unlock() } |
︙ | ︙ |
Changes to web/content/content.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- // Package content manages content handling within the web package. // It translates syntax values into content types, and vice versa. package content import ( |
︙ | ︙ |
Changes to web/content/content_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package content_test import ( "testing" |
︙ | ︙ |
Changes to web/server/impl/http.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "context" "net" |
︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | writeTimeout = 10 * time.Second idleTimeout = 120 * time.Second ) // httpServer is a HTTP server. type httpServer struct { http.Server } // initializeHTTPServer creates a new HTTP server object. func (srv *httpServer) initializeHTTPServer(addr string, handler http.Handler) { if addr == "" { addr = ":http" } srv.Server = http.Server{ Addr: addr, Handler: handler, // See: https://blog.cloudflare.com/exposing-go-on-the-internet/ ReadTimeout: readTimeout, WriteTimeout: writeTimeout, IdleTimeout: idleTimeout, } } // SetDebug enables debugging goroutines that are started by the server. // Basically, just the timeout values are reset. This method should be called // before running the server. func (srv *httpServer) SetDebug() { srv.ReadTimeout = 0 | > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | writeTimeout = 10 * time.Second idleTimeout = 120 * time.Second ) // httpServer is a HTTP server. type httpServer struct { http.Server waitStop chan struct{} } // initializeHTTPServer creates a new HTTP server object. func (srv *httpServer) initializeHTTPServer(addr string, handler http.Handler) { if addr == "" { addr = ":http" } srv.Server = http.Server{ Addr: addr, Handler: handler, // See: https://blog.cloudflare.com/exposing-go-on-the-internet/ ReadTimeout: readTimeout, WriteTimeout: writeTimeout, IdleTimeout: idleTimeout, } srv.waitStop = make(chan struct{}) } // SetDebug enables debugging goroutines that are started by the server. // Basically, just the timeout values are reset. This method should be called // before running the server. func (srv *httpServer) SetDebug() { srv.ReadTimeout = 0 |
︙ | ︙ |
Changes to web/server/impl/impl.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- // Package impl provides the Zettelstore web service. package impl import ( "context" |
︙ | ︙ |
Changes to web/server/impl/router.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package impl import ( "io" "net/http" |
︙ | ︙ | |||
185 186 187 188 189 190 191 | } if len(t) == 0 { rt.log.Debug().Msg("no auth token found in request") // IP already logged: ServeHTTP return r } tokenData, err := rt.auth.CheckToken(t, k) if err != nil { | | | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | } if len(t) == 0 { rt.log.Debug().Msg("no auth token found in request") // IP already logged: ServeHTTP return r } tokenData, err := rt.auth.CheckToken(t, k) if err != nil { rt.log.Sense().Err(err).HTTPIP(r).Msg("invalid auth token") return r } ctx := r.Context() user, err := rt.ur.GetUser(ctx, tokenData.Zid, tokenData.Ident) if err != nil { rt.log.Sense().Zid(tokenData.Zid).Str("ident", tokenData.Ident).Err(err).HTTPIP(r).Msg("auth user not found") return r } return r.WithContext(updateContext(ctx, user, &tokenData)) } func getSessionToken(r *http.Request) []byte { cookie, err := r.Cookie(sessionName) |
︙ | ︙ |
Changes to web/server/server.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package server provides the Zettelstore web service. package server import ( "context" |
︙ | ︙ |
Changes to www/build.md.
︙ | ︙ | |||
20 21 22 23 24 25 26 | Let's assume, you have created `$HOME/fossils`. 1. Clone the repository: `fossil clone https://zettelstore.de/ $HOME/fossils/zettelstore.fossil`. 1. Create a working directory. Let's assume, you have created `$HOME/zettelstore`. 1. Change into this directory: `cd $HOME/zettelstore`. 1. Open development: `fossil open $HOME/fossils/zettelstore.fossil`. | | | | | | > > > | | < < < < < < < < < < < < | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | Let's assume, you have created `$HOME/fossils`. 1. Clone the repository: `fossil clone https://zettelstore.de/ $HOME/fossils/zettelstore.fossil`. 1. Create a working directory. Let's assume, you have created `$HOME/zettelstore`. 1. Change into this directory: `cd $HOME/zettelstore`. 1. Open development: `fossil open $HOME/fossils/zettelstore.fossil`. ## The build tool In the directory `tools` there is a Go file called `build.go`. It automates most aspects, (hopefully) platform-independent. The script is called as: ``` go run tools/build.go [-v] COMMAND ``` The flag `-v` enables the verbose mode. It outputs all commands called by the tool. Some important `COMMAND`s are: * `build`: builds the software with correct version information and puts it into a freshly created directory `bin`. * `check`: checks the current state of the working directory to be ready for release (or commit). * `clean`: removes the build directories and cleans the Go cache. * `version`: prints the current version information. * `tools`: installs / updates the tools described above: staticcheck, shadow, unparam, govulncheck. Therefore, the easiest way to build your own version of the Zettelstore software is to execute the command ``` go run tools/build.go build ``` In case of errors, please send the output of the verbose execution: ``` go run tools/build.go -v build ``` ## A note on the use of Fossil Zettelstore is managed by the Fossil version control system. Fossil is an alternative to the ubiquitous Git version control system. However, Go seems to prefer Git and popular platforms that just support Git. Some dependencies of Zettelstore, namely [Zettelstore client](https://zettelstore.de/client) and [sx](https://zettelstore.de/sx), are |
︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Change Log</title> <a id="0_17"></a> <h2>Changes for Version 0.17.0 (pending)</h2> <a id="0_16"></a> <h2>Changes for Version 0.16.0 (2023-11-30)</h2> * Sx function <code>define</code> is removed, as announced for version 0.15.0. Use <code>defvar</code> (to define variables) or <code>defun</code> (to define functions) instead. In addition <code>defunconst</code> defines a constant function, which ensures a fixed binding of its name to its function body (performance optimization). (breaking: webui) |
︙ | ︙ | |||
76 77 78 79 80 81 82 | timestamps, e.g. for YYYY or YYYYMM. (minor) * SHTML encoder fixed w.r.t inline quoting. Previously, an <q> tag was used, which is inappropriate. Restored smart quotes from version 0.3, but with new SxHTML infrastructure. This affect the html encoder and the WebUI too. Now, an empty quote should not result in a warning by HTML linters. (minor: api, webui) | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | timestamps, e.g. for YYYY or YYYYMM. (minor) * SHTML encoder fixed w.r.t inline quoting. Previously, an <q> tag was used, which is inappropriate. Restored smart quotes from version 0.3, but with new SxHTML infrastructure. This affect the html encoder and the WebUI too. Now, an empty quote should not result in a warning by HTML linters. (minor: api, webui) * Add new zettelmarkup inline formatting: <tt>##Text##</tt> will mark / highlight the given Text. It is typically used to highlight some text, which is important for you, but not for the original author. When rendered as HTML, the <mark> tag is used. (minor: zettelmarkup) * Add configuration keys to show, not to show, or show the closed list of referencing zettel in the web user interface. You can set these configurations system-wide, per user, or per zettel. Often it is used to ensure a “clean” home zettel. Affects the list of incoming / back links, folge zettel, subordinate zettel, and successor zettel. (minor: webui) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_15"></a> <h2>Changes for Version 0.15.0 (2023-10-26)</h2> * Sx function <tt>define</tt> is now deprecated. It will be removed in version 0.16. Use <tt>defvar</tt> or <tt>defun</tt> instead. Otherwise the WebUI will not work in version 0.16. (major: webui, deprecated) * Zettel can be re-indexed via WebUI or API query action <tt>REINDEX</tt>. The info page of a zettel contains a link to re-index the zettel. In a query transclusion, this action is ignored. (major: api, webui). * Allow to determine a tag zettel for a given tag. (major: api, webui) * Present user the option to create a (missing) tag zettel (in list view). Results in a new predefined zettel with identifier 00000000090003, which is a template for new tag zettel. (minor: webui) * ZIP file with manual now contains a zettel 00001000000000 that contains its build date (metadata key <tt>created</tt>) and version (in the zettel content) (minor) * If an error page cannot be created due to template errors (or similar), a plain text error page is delivered instead. It shows the original error and the error that occured durng rendering the original error page. (minor: webui) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_14"></a> <h2>Changes for Version 0.14.0 (2023-09-22)</h2> * Remove support for JSON. This was marked deprecated in version 0.12.0. Use the <tt>data</tt> encoding instead, a form of symbolic expressions. (breaking: api; minor: webui) * Remove deprecated syntax for a context list: <tt>CONTEXT zid</tt>. Use <tt>zid CONTEXT</tt> instead. It was deprecated in version 0.13.0. (breaking: api, webui, zettelmarkup) * Replace CSS-role-map mechanism with a more general Sx-based one: user specific code may generates parts of resulting HTML document. (breaking: webui) * Allow meta-tags, i.e. zettel for a specific tag. Meta-tags have the tag name as a title and specify the role "tag". (major: webui) * Allow to load sx code from multiple zettel; dependencies are specified using <tt>precursor</tt> metadata. (major: webui) * Allow sx code to change WebUI for zettel with specified role. (major: webui) * Some minor usability improvements. (minor: webui) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_13"></a> <h2>Changes for Version 0.13.0 (2023-08-07)</h2> * There are for new search operators: less, not less, greater, not greater. These use the same syntax as the operators prefix, not prefix, suffix, not suffix. The latter are now denoted as <tt>[</tt>, <tt>![</tt>, <tt>]</tt>, and <tt>!]</tt>. The first may operate numerically for metadata like numbers, timestamps, and zettel identifier. They are not supported for full-test search. (breaking: api, webui) * The API endpoint <tt>/o/{ID}</tt> (order of zettel ID) is no longer available. Please use the query expression <tt>{ID} ITEMS</tt> instead. (breaking: api) * The API endpoint <tt>/u/{ID}</tt> (unlinked references of zettel ID) is no longer available. Please use the query expression <tt>{ID} UNLINKED</tt> instead. (breaking: api) * All API endpoints allow to encode zettel data with the <tt>data</tt> encodings, incl. creating, updating, retrieving, and querying zettel. (major: api) * Change syntax for context query to <tt>zid ... CONTEXT</tt>. This will allow to add more directives that operate on zettel identifier. Old syntax <tt>CONTEXT zid</tt> will be removed in 0.14. (major, deprecated) * Add query directive <tt>ITEMS</tt> that will produce a list of metadata of all zettel that are referenced by the originating zettel in a top-level list. It replaces the API endpoint <tt>/o/{ID}</tt> (and makes it more useful). (major: api, webui) * Add query directive <tt>UNLINKED</tt> that will produce a list of metadata of all zettel that are mentioning the originating zettel in a top-level, but do not mention them. It replaces the API endpoint <tt>/u/{ID}</tt> (and makes it more useful). (major: api, webui) * Add query directive <tt>IDENT</tt> to distinguish a search for a zettel identifier (“{ID}”), that will list all metadata of zettel containing that zettel identifier, and a request to just list the metadata of given zettel (“{ID} IDENT”). The latter could be filtered further. (minor: api, webui) * Add support for metadata key <tt>folge-role</tt>. (minor) * Allow to create a child from a given zettel. (minor: webui) * Make zettel entry/edit form a little friendlier: auto-prepend missing '#' to tags; ensure that role and syntax receive just a word. (minor: webui) * Use a zettel that defines builtins for evaluating WebUI templates. (minor: webui) * Add links to retrieve result of a query in other formats. (minor: webui) * Always log the found configuration file. (minor: server) * The use of the <tt>json</tt> zettel encoding is deprecated (since version 0.12.0). Support for this encoding will be removed in version 0.14.0. Please use the new <tt>data</tt> encoding instead. (deprecated: api) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_12"></a> <h2>Changes for Version 0.12.0 (2023-06-05)</h2> * Syntax of templates for the web user interface are changed from Mustache |
︙ | ︙ | |||
222 223 224 225 226 227 228 | that is the short form for "symbolic expression for HTML". (breaking) * Render footer zettel on all WebUI pages. (fix: webui) * Query search operator "=" now compares for equality, ":" compares depending on the value type. (minor: api, webui) | | | | | < | | | | < | < | | | | | > | | | | | | | | | | | | < | | | | | | < | | | | | | < | | | | | | | | | | | | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | that is the short form for "symbolic expression for HTML". (breaking) * Render footer zettel on all WebUI pages. (fix: webui) * Query search operator "=" now compares for equality, ":" compares depending on the value type. (minor: api, webui) * Search term <tt>PICK</tt> now respects the original sort order. This makes it more useful and orthogonal to <tt>RANDOM</tt> and <tt>LIMIT</tt>. As a side effect, zettel lists retrieved via the API are no longer sorted. In case you want a specific order, you must specify it explicit. (minor: api, webui) * New metadata key <tt>expire</tt> records a timestamp when a zettel should be treated as, well, expired. (minor) * New metadata keys <tt>superior</tt> and <tt>subordinate</tt> (calculated from <tt>superior</tt>) allow to specify a hierarchy between zettel. (minor) * Metadata keys with suffix <tt>-date</tt> and <tt>-time</tt> are treated as timestamp values. (minor) * <tt>sexpr</tt> zettel encoding is now documented in the manual. (minor: manual) * Build tool allows to install / update external Go tools needed to build the software. (minor) * Show only useful metadata on WebUI, not the internal metadata. (minor: webui) * The use of the <tt>json</tt> zettel encoding is deprecated. Support for this encoding may be removed in future versions. Please use the new <tt>data</tt> encoding instead. (deprecated: api) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_11"></a> <h2>Changes for Version 0.11.2 (2023-04-16)</h2> * Render footer zettel on all WebUI pages. Backported from 0.12.0. Many thanks to HK for reporting it! (fix: webui) <h2>Changes for Version 0.11.1 (2023-03-28)</h2> * Make <tt>PICK</tt> search term a little bit more deterministic so that the “Save As Zettel” button produces the same list. (fix: webui) <h2>Changes for Version 0.11.0 (2023-03-27)</h2> * Remove ZJSON encoding. It was announced in version 0.10.0. Use Sexpr encoding instead. (breaking) * Title of a zettel is no longer interpreted as Zettelmarkup text. Now it is just a plain string, possibly empty. Therefore, no inline formatting (like bold text), no links, no footnotes, no citations (the latter made rendering the title often questionable, in some contexts). If you used special entities, please use the unicode characters directly. However, as a good practice, it is often the best to printable ASCII characters. (breaking) * Remove runtime configuration <tt>marker-external</tt>. It was added in version [#0_0_6|0.0.6] and updated in [#0_0_10|0.0.10]. If you want to change the marker for an external URL, you could modify zettel 00000000020001 (Zettelstore Base CSS) or zettel 00000000025001 (Zettelstore User CSS, preferred) by changing / adding a rule to add some content after an external <tt><a ...></tt> tag. (breaking: webui) * Add SHTML encoding. This allows to ensure the quality of generated HTML code. In addition, clients might use it, because it is easier to parse and manipulate than ordinary HTML. In the future, HTML template zettel will probably also use SHTML, deprecating the current Mustache syntax (which was added in [#0_0_9|0.0.9]). (major) * Search term <tt>PICK n</tt>, where <tt>n</tt> is an integer value greater zero, will pick randomly <tt>n</tt> elements from the search result list. Somehow similar (and faster) as <tt>RANDOM LIMIT n</tt>, but allows also later ordering of the resulting list. (minor) * Changed cost model for zettel context: a zettel with more outgoing/incoming references has higher cost than a zettel with less references. Also added support for traversing tags, with a similar cost model. As an effect, zettel hubs (in many cases your home zettel) will less likely add its references. Same for often used tags. The cost model might change in some details in the future, but the idea of a penalty applied to zettel / tags with many references will hold. (minor) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_10"></a> <h2>Changes for Version 0.10.1 (2023-01-30)</h2> * Show button to save a query into a zettel only when the current user has authorization to do it. (fix: webui) <h2>Changes for Version 0.10.0 (2023-01-24)</h2> * Remove support for endpoints <tt>/j, /m, /q, /p, /v</tt>. Their functions are merged into endpoint <tt>/z</tt>. This was announced in version 0.9.0. Please use only client library with at least version 0.10.0 too. (breaking: api) * Remove support for runtime configuration key <tt>footer-html</tt>. Use <tt>footer-zettel</tt> instead. Deprecated in version 0.9.0. (breaking: webui) * Save a query into a zettel to freeze it. (major: webui) * Allow to show all used metadata keys, linked with their occurrences and their values. (minor: webui) * Mark ZJSON encoding as deprecated for v0.11.0. Please use Sexpr encoding instead. (deprecated) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_9"></a> <h2>Changes for Version 0.9.0 (2022-12-12)</h2> * Remove support syntax <tt>pikchr</tt>. Although it was a nice idea to include it into Zettelstore, the implementation is too brittle (w.r.t. the expected long lifetime of Zettelstore). There should be other ways to support SVG front-ends. (breaking) * Allow to upload content when creating / updating a zettel. (major: webui) * Add syntax “draw” (again) (minor: zettelmarkup) * Allow to encode zettel in Markdown. Please note: not every aspect of a zettel can be encoded in Markdown. Those aspects will be ignored. (minor: api) * Enhance zettel context by raising the importance of folge zettel (and similar). (minor: api, webui) * Interpret zettel files with extension <tt>.webp</tt> as an binary image file format. (minor) * Allow to specify service specific log level via statup configuration and via command line. (minor) * Allow to specify a zettel to serve footer content via runtime comfiguration <tt>footer-zettel</tt>. Can be overwritten by user zettel. (minor: webui) * Footer data is automatically separated by a thematic break / horizontal rule. If you do not like it, you have to update the base template. (minor: webui) * Allow to set runtime configuration <tt>home-zettel</tt> in the user zettel to make it user-specific. (minor: webui) * Serve favicon.ico from the asset directory. (minor: webui) * Zettelmarkup cheat sheet (minor: manual) * Runtime configuration key <tt>footer-html</tt> will be removed in Version 0.10.0. Please use <tt>footer-zettel</tt> instead. (deprecated: webui) * In the next version 0.10.0, the API endpoints for a zettel (<tt>/j</tt>, <tt>/p</tt>, <tt>/v</tt>) will be merged with endpoint <tt>/z</tt>. Basically, the previous endpoint will be refactored as query parameter of endpoint <tt>/z</tt>. To reduce errors, there will be no version, where the previous endpoint are still available and the new funnctionality is still there. This is a warning to prepare for some breaking changes in v0.10.0. This also affects the API client implementation. (warning: api) * Some smaller bug fixes and improvements, to the software and to the documentation. <a id="0_8"></a> <h2>Changes for Version 0.8.0 (2022-10-20)</h2> * Remove support for tags within zettel content. Removes also property metadata keys <tt>all-tags</tt> and <tt>computed-tags</tt>. Deprecated in version 0.7.0. (breaking: zettelmarkup, api, webui) * Remove API endpoint <tt>/m</tt>, which retrieve aggregated (tags, roles) zettel identifier. Deprecated in version 0.7.0. (breaking: api) * Remove support for URL query parameter starting with an underscore. Deprecated in version 0.7.0. (breaking: api, webui) * Ignore HTML content by default, and allow HTML gradually by setting startup value <tt>insecure-html</tt>. (breaking: markup) * Endpoint <tt>/q</tt> returns list of full metadata, if no query action is specified. A HTTP call <tt>GET /z</tt> (retrieving metadata of all or some zettel) is now an alias for <tt>GET /q</tt>. (major: api) * Allow to create a zettel that acts as the new version of an existing zettel. Useful if you want to have access to older, outdated content. (minor: webui) * Allow transclusion to reference local image via URL. (minor: zettelmarkup, webui) * Add categories in RSS feed, based on zettel tags. |
︙ | ︙ | |||
429 430 431 432 433 434 435 | <a id="0_7"></a> <h2>Changes for Version 0.7.1 (2022-09-18)</h2> * Produce a RSS feed compatible to Miniflux. (minor) * Make sure to always produce a pubdata in RSS feed. (bug) * Prefix search for data that looks like a zettel identifier may end with a | | | | | | | | | < | | | | | | | | | | | | < | | | | | | | | | | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 | <a id="0_7"></a> <h2>Changes for Version 0.7.1 (2022-09-18)</h2> * Produce a RSS feed compatible to Miniflux. (minor) * Make sure to always produce a pubdata in RSS feed. (bug) * Prefix search for data that looks like a zettel identifier may end with a <tt>0</tt>. (bug) * Fix glitch on manual zettel. (bug) <h2>Changes for Version 0.7.0 (2022-09-17)</h2> * Removes support for URL query parameter to search for metadata values, sorting, offset, and limit a zettel list. Deprecated in version 0.6.0 (breaking: api, webui) * Allow to search for the existence / non-existence of a metadata key with the "?" operator: <tt>key?</tt> and <tt>key!?</tt>. Previously, the ":" operator was used for this by specifying an empty search value. Now you can use the ":" operator to find empty / non-empty metadata values. If you specify a search operator for metadata, the specified key is assumed to exist. (breaking: api, webui) * Rename “search expression” into “query expressions”. Similar, the reference prefix <tt>search:</tt> to specify a query link or a query transclusion is renamed to <tt>query:</tt> (breaking: zettelmarkup) * Rename query parameter for query expression from <tt>_s</tt> to <tt>q</tt>. (breaking: api, webui) * Cleanup names for HTTP query parameters in WebUI. Update your bookmarks if you used them. (For API: see below) (breaking: webui) * Allow search terms to be OR-ed. This allows to specify any search expression in disjunctive normal form. Therefore, the NEGATE term is not needed any more. (breaking: api, webui) * Replace runtime configuration <tt>default-lang</tt> with <tt>lang</tt>. Additionally, <tt>lang</tt> set at the zettel of the current user, will provide a default value for the current user, overwriting the global default value. (breaking) * Add new syntax <tt>pikchr</tt>, a markup language for diagrams in technical documentation. (major) * Add endpoint <tt>/q</tt> to query the zettelstore and aggregate resulting values. This is done by extending the query syntax. (major: api) * Add support for query actions. Actions may aggregate w.r.t. some metadata keys, or produce an RSS feed. (major: api, webui) * Query results can be ordered for more than one metadata key. Ordering by zettel identifier is an implicit last order expression to produce stable results. (minor: api, webui) * Add support for an asset directory, accessible via URL prefix <tt>/assests/</tt>. (minor: server) * Add support for metadata key <tt>created</tt>, a timestamp when the zettel was created. Since key <tt>published</tt> is now either <tt>created</tt> or <tt>modified</tt>, it will now always contains a valid time stamp. (minor) * Add support for metadata key <tt>author</tt>. It will be displayed on a zettel, if set. (minor: webui) * Remove CSS for lists. The browsers default value for <tt>padding-left</tt> will be used. (minor: webui) * Removed templates for rendering roles and tags lists. This is now done by query actions. (minor: webui) * Tags within zettel content are deprecated in version 0.8. This affects the computed metadata keys <tt>content-tags</tt> and <tt>all-tags</tt>. They will be removed. The number sign of a content tag introduces unintended tags, esp. in the english language; content tags may occur within links → links within links, when rendered as HTML; content tags may occur in the title of a zettel; naming of content tags, zettel tags, and their union is confusing for many. Migration: use zettel tags or replace content tag with a search. (deprecated: zettelmarkup) * Cleanup names for HTTP query parameter for API calls. Essentially, underscore characters in front are removed. Please use new names, old names will be deprecated in version 0.8. (deprecated: api) * Some smaller bug fixes and improvements, to the software and to the documentation. |
︙ | ︙ | |||
574 575 576 577 578 579 580 | * If authentication is enabled, a secret of at least 16 bytes must be set in the startup configuration. (breaking) * “Sexpr” encoding replaces “Native” encoding. Sexpr encoding is much easier to parse, compared with native and ZJSON encoding. In most cases it is smaller than ZJSON. (breaking: api) | | | | | | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | * If authentication is enabled, a secret of at least 16 bytes must be set in the startup configuration. (breaking) * “Sexpr” encoding replaces “Native” encoding. Sexpr encoding is much easier to parse, compared with native and ZJSON encoding. In most cases it is smaller than ZJSON. (breaking: api) * Endpoint <tt>/r</tt> is changed to <tt>/m?_key=role</tt> and returns now a map of role names to the list of zettel having this role. Endpoint <tt>/t</tt> is changed to <tt>/m?_key=tags</tt>. It already returned mapping described before. (breaking: api) * Remove support for a default value for metadata key title, role, and syntax. Title and role are now allowed to be empty, an empty syntax value defaults to “plain”. (breaking) * Add support for an “evaluation block” syntax in Zettelmarkup to allow interpretation of content by external software. |
︙ | ︙ | |||
622 623 624 625 626 627 628 | documentation. <a id="0_4"></a> <h2>Changes for Version 0.4 (2022-03-08)</h2> * Encoding “djson” renamed to “zjson” (<em>zettel json</em>). (breaking: api; minor: webui) | | | | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | documentation. <a id="0_4"></a> <h2>Changes for Version 0.4 (2022-03-08)</h2> * Encoding “djson” renamed to “zjson” (<em>zettel json</em>). (breaking: api; minor: webui) * Remove inline quotation syntax <tt><<...<<</tt>. Now, <tt>""...""</tt> generates the equivalent code. Typographical quotes are generated by the browser, not by Zettelstore. (breaking: Zettelmarkup) * Remove inline formatting for monospace. Its syntax is now used by the similar syntax element of literal computer input. Monospace was just a visual element with no semantic association. Now, the syntax <kbd>++...++</kbd> is obsolete. (breaking: Zettelmarkup). |
︙ | ︙ | |||
654 655 656 657 658 659 660 | interpreted as Zettelmarkup. Similar, the suffix <kbd>-set</kbd> denotes a set/list of words and the suffix <kbd>-zids</kbd> a set/list of zettel identifier. (minor: api, webui) * Change generated URLs for zettel-creation forms. If you have bookmarked them, e.g. to create a new zettel, you should update. (minor: webui) | | | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 | interpreted as Zettelmarkup. Similar, the suffix <kbd>-set</kbd> denotes a set/list of words and the suffix <kbd>-zids</kbd> a set/list of zettel identifier. (minor: api, webui) * Change generated URLs for zettel-creation forms. If you have bookmarked them, e.g. to create a new zettel, you should update. (minor: webui) * Remove support for metadata key <tt>no-index</tt> to suppress indexing selected zettel. It was introduced in <a href="#0_0_11">v0.0.11</a>, but disallows some future optimizations for searching zettel. (minor: api, webui) * Make some metadata-based searches a little bit faster by executing a (in-memory-based) full-text search first. Now only those zettel are loaded from file that contain the metdata value. (minor: api, webui) * Add an API call to retrieve the version of the Zettelstore. (minor: api) * Limit the amount of zettel and bytes to be stored in a memory box. Allows to use it with public access. (minor: box) * Disallow to cache the authentication cookie. Will remove most unexpected log-outs when using a mobile device. (minor: webui) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_3"></a> <h2>Changes for Version 0.3 (2022-02-09)</h2> * Zettel files with extension <tt>.meta</tt> are now treated as content files. Previoulsy, they were interpreted as metadata files. The interpretation as metadata files was deprecated in version 0.2. (breaking: directory and file/zip box) * Add syntax “draw” to produce some graphical representations. (major) * Add Zettelmarkup syntax to specify full transclusion of other zettel. (major: Zettelmarkup) |
︙ | ︙ | |||
698 699 700 701 702 703 704 | (minor: directory and file/zip box) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_2"></a> <h2>Changes for Version 0.2 (2022-01-19)</h2> * v0.2.1 (2021-02-01) updates the license year in some documents | | | | | | | | | | | | < | | | | | | | | | < | | | | | | | | | | < | | | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | (minor: directory and file/zip box) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_2"></a> <h2>Changes for Version 0.2 (2022-01-19)</h2> * v0.2.1 (2021-02-01) updates the license year in some documents * Remove support for <tt>;;small text;;</tt> Zettelmarkup. (breaking: Zettelmarkup) * On macOS, the downloadable executable program is now called “zettelstore”, as on all other Unix-like platforms. (possibly breaking: macOS) * External metadata (e.g. for zettel with file extension other than <tt>.zettel</tt>) are stored in files without an extension. Metadata files with extension <tt>.meta</tt> are still recognized, but result in a warning message. In a future version (probably v0.3), <tt>.meta</tt> files will be treated as ordinary content files, possibly resulting in duplicate content. In other words: usage of <tt>.meta</tt> files for storing metadata is deprecated. (possibly breaking: directory and file box) * Show unlinked references in info page of each zettel. Unlinked references are phrases within zettel content that might reference another zettel with the same title as the phase. (major: webui) * Add endpoint <tt>/u/{ID}</tt> to retrieve unlinked references. (major: api) * Provide a logging facility. Log messages are written to standard output. Messages with level “information” are also written to a circular buffer (of length 8192) which can be retrieved via a computed zettel. There is a command line flag <tt>-l LEVEL</tt> to specify an application global logging level on startup (default: “information”). Logging level can also be changed via the administrator console, even for specific (sub-) services. (major) * The internal handling of zettel files is rewritten. This allows less reloads ands detects when the directory containing the zettel files is removed. The API, WebUI, and the admin console allow to manually refresh the internal state on demand. (major: box, webui) * <tt>.zettel</tt> files with YAML header are now correctly written. (bug) * Selecting zettel based on their metadata allows the same syntax as searching for zettel content. For example, you can list all zettel that have an identifier not ending with <tt>00</tt> by using the query <tt>id=!<00</tt>. (minor: api, webui) * Remove support for <tt>//deprecated emphasized//</tt> Zettelmarkup. (minor: Zettelmarkup) * Add options to profile the software. Profiling can be enabled at the command line or via the administrator console. (minor) * Add computed zettel that lists all supported parser / recognized zettel syntaxes. (minor) * Add API call to check for enabled authentication. (minor: api) * Renewing an API access token works even if authentication is not enabled. This corresponds to the behaviour of optaining an access token. (minor: api) * If there is nothing to return, use HTTP status code 204, instead of 200 + <tt>Content-Length: 0</tt>. (minor: api) * Metadata key <tt>duplicates</tt> stores the duplicate file names, instead of just a boolean value that there were duplicate file names. (minor) * Document autostarting Zettelstore on Windows, macOS, and Linux. (minor) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_1"></a><a id="0_1_0"></a> <h2>Changes for Version 0.1 (2021-11-11)</h2> * v0.1.3 (2021-12-15) fixes a bug where the modification date could be set when a new zettel is created. * v0.1.2 (2021-11-18) fixes a bug when selecting zettel from a list when more than one comparison is negated. * v0.1.1 (2021-11-12) updates the documentation, mostly related to the deprecation of the <tt>//</tt> markup. * Remove visual Zettelmarkup (italic, underline). Semantic Zettelmarkup (emphasize, insert) is still allowed, but got a different syntax. The new syntax for <ins>inserted text</ins> is <tt>>>inserted>></tt>, while its previous syntax now denotes <em>emphasized text</em>: <tt>__emphasized__</tt>. The previous syntax for emphasized text is now deprecated: <tt>//deprecated emphasized//</tt>. Starting with Version 0.2.0, the deprecated syntax will not be supported. The reason is the collision with URLs that also contain the characters <tt>//</tt>. The ZMK encoding of a zettel may help with the transition (<tt>/v/{ZettelID}?_part=zettel&_enc=zmk</tt>, on the Info page of each zettel in the WebUI). Additionally, all deprecated uses of <tt>//</tt> will be rendered with a dashed box within the WebUI. (breaking: Zettelmarkup). * API client software is now a [https://zettelstore.de/client/|separate] project. (breaking) * Initial support for HTTP security headers (Content-Security-Policy, Permissions-Policy, Referrer-Policy, X-Content-Type-Options, X-Frame-Options). Header values are currently some constant values. (possibly breaking: api, webui) * Remove visual Zettelmarkup (bold, striketrough). Semantic Zettelmarkup (strong, delete) is still allowed and replaces the visual elements syntactically. The visual appearance should not change (depends on your changes / additions to CSS zettel). (possibly breaking: Zettelmarkup). * Add API endpoint <tt>POST /v</tt> to retrieve HTMl and text encoded strings from given ZettelMarkup encoded values. This will be used to render a HTML page from a given zettel: in many cases the title of a zettel must be treated separately. (minor: api) * Add API endpoint <tt>/m</tt> to retrieve only the metadata of a zettel. (minor: api) * New metadata value <tt>content-tags</tt> contains the tags that were given in the zettel content. To put it simply, <tt>all-tags</tt> = <tt>tags</tt> + <tt>content-tags</tt>. (minor) * Calculating the context of a zettel stops at the home zettel. (minor: api, webui) * When renaming or deleting a zettel, a warning will be given, if other zettel references the given zettel, or when “deleting” will uncover zettel in overlay box. (minor: webui) |
︙ | ︙ | |||
832 833 834 835 836 837 838 | (info) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_15"></a> <h2>Changes for Version 0.0.15 (2021-09-17)</h2> * Move again endpoint characters for authentication to make room for future | | < | | < > | > | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | < | | | | | | | < | < | | | < | | | < | | | | | | | | | | < | | < | | | < | < | < | | < | < | < | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 | (info) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_15"></a> <h2>Changes for Version 0.0.15 (2021-09-17)</h2> * Move again endpoint characters for authentication to make room for future features. WebUI authentication moves from <tt>/a</tt> to <tt>/i</tt> (login) and <tt>/i?logout</tt> (logout). API authentication moves from <tt>/v</tt> to </tt>/a</tt>. JSON-based basic zettel handling moves from <tt>/z</tt> to <tt>/j</tt> and <tt>/z/{ID}</tt> to <tt>/j/{ID}</tt>. Since the API client is updated too, this should not be a breaking change for most users. (minor: api, webui; possibly breaking) * Add API endpoint <tt>/v/{ID}</tt> to retrieve an evaluated zettel in various encodings. Mostly replaces endpoint <tt>/z/{ID}</tt> for other encodings except “json” and “raw”. Endpoint <tt>/j/{ID}</tt> now only returns JSON data, endpoint <tt>/z/{ID}</tt> is used to retrieve plain zettel data (previously called “raw”). See documentation for details. (major: api; breaking) * Metadata values of type <em>tag set</em> (the metadata with key <tt>tags</tt> is its most prominent example), are now compared in a case-insensitive manner. Tags that only differ in upper / lower case character are now treated identical. This might break your workflow, if you depend on case-sensitive comparison of tag values. Tag values are translated to their lower case equivalent before comparing them and when you edit a zettel through Zettelstore. If you just modify the zettel files, your tag values remain unchanged. (major; breaking) * Endpoint <tt>/z/{ID}</tt> allows the same methods as endpoint <tt>/j/{ID}</tt>: <tt>GET</tt> retrieves zettel (see above), <tt>PUT</tt> updates a zettel, <tt>DELETE</tt> deletes a zettel, <tt>MOVE</tt> renames a zettel. In addtion, <tt>POST /z</tt> will create a new zettel. When zettel data must be given, the format is plain text, with metadata separated from content by an empty line. See documentation for more details. (major: api (plus WebUI for some details)) * Allows to transclude / expand the content of another zettel into a target zettel when the zettel is rendered. By using the syntax of embedding an image (which is some kind of expansion too), the first top-level paragraph of a zettel may be transcluded into the target zettel. Endless recursion is checked, as well as a possible “transclusion bomb ” (similar to a XML bomb). See manual for details. (major: zettelmarkup) * The endpoint <tt>/z</tt> allows to list zettel in a simpler format than endpoint <tt>/j</tt>: one line per zettel, and only zettel identifier plus zettel title. (minor: api) * Folgezettel are now displayed with full title at the bottom of a page. (minor: webui) * Add API endpoint <tt>/p/{ID}</tt> to retrieve a parsed, but not evaluated zettel in various encodings. (minor: api) * Fix: do not list a shadowed zettel that matches the select criteria. (minor) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_14"></a> <h2>Changes for Version 0.0.14 (2021-07-23)</h2> * Rename “place” into “box”. This also affects the configuration keys to specify boxes <tt>box-uri<em>X</em></tt> (previously <tt>place-uri-<em>X</em></tt>. Older changes documented here are renamed too. (breaking) * Add API for creating, updating, renaming, and deleting zettel. (major: api) * Initial API client for Go. (major: api) * Remove support for paging of WebUI list. Runtime configuration key <tt>list-page-size</tt> is removed. If you still specify it, it will be ignored. (major: webui) * Use endpoint <tt>/v</tt> for user authentication via API. Endpoint <tt>/a</tt> is now used for the web user interface only. Similar, endpoint <tt>/y</tt> (“zettel context”) is renamed to <tt>/x</tt>. (minor, possibly breaking) * Type of used-defined metadata is determined by suffix of key: <tt>-number</tt>, <tt>-url</tt>, <tt>-zid</tt> will result the values to be interpreted as a number, an URL, or a zettel identifier. (minor, but possibly breaking if you already used a metadata key with above suffixes, but as a string type) * New <tt>user-role</tt> “creator”, which is only allowed to create new zettel (except user zettel). This role may only read and update public zettel or its own user zettel. Added to support future client software (e.g. on a mobile device) that automatically creates new zettel but, in case of a password loss, should not allow to read existing zettel. (minor, possibly breaking, because new zettel template zettel must always prepend the string <tt>new-</tt> before metdata keys that should be transferred to the new zettel) * New suported metadata key <tt>box-number</tt>, which gives an indication from which box the zettel was loaded. (minor) * New supported syntax <tt>html</tt>. (minor) * New predefined zettel “User CSS” that can be used to redefine some predefined CSS (without modifying the base CSS zettel). (minor: webui) * When a user moves a zettel file with additional characters into the box directory, these characters are preserved when zettel is updated. (bug) * The phase “filtering a zettel list” is more precise “selecting zettel” (documentation) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_13"></a> <h2>Changes for Version 0.0.13 (2021-06-01)</h2> * Startup configuration <tt>box-<em>X</em>-uri</tt> (where <em>X</em> is a number greater than zero) has been renamed to <tt>box-uri-<em>X</em></tt>. (breaking) * Web server processes startup configuration <tt>url-prefix</tt>. There is no need for stripping the prefix by a front-end web server any more. (breaking: webui, api) * Administrator console (only optional accessible locally). Enable it only on systems with a single user or with trusted users. It is disabled by default. (major: core) * Remove visibility value “simple-expert” introduced in [#0_0_8|version 0.0.8]. It was too complicated, esp. authorization. There was a name collision with the “simple” directory box sub-type. (major) * For security reasons, HTML blocks are not encoded as HTML if they contain certain snippets, such as <tt><script</tt> or <tt><iframe</tt>. These may be caused by using CommonMark as a zettel syntax. (major) * Full-text search can be a prefix search or a search for equal words, in addition to the search whether a word just contains word of the search term. (minor: api, webui) * Full-text search for URLs, with above additional operators. (minor: api, webui) * Add system zettel about license, contributors, and dependencies (and their license). For a nicer layout of zettel identifier, the zettel about environment values and about runtime metrics got new zettel identifier. This affects only user that referenced those zettel. (minor) * Local images that cannot be read (not found or no access rights) are substituted with the new default image, a spinning emoji. See [/file?name=box/constbox/emoji_spin.gif]. (minor: webui) * Add zettelmarkup syntax for a table row that should be ignored: <tt>|%</tt>. This allows to paste output of the administrator console into a zettel. (minor: zmk) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_12"></a> <h2>Changes for Version 0.0.12 (2021-04-16)</h2> * Raise the per-process limit of open files on macOS to 1.048.576. This allows most macOS users to use at least 500.000 zettel. That should be enough for the near future. (major) * Mitigate the shortcomings of the macOS version by introducing types of directory boxes. The original directory box type is now called "notify" (the default value). There is a new type called "simple". This new type does not notify Zettelstore when some of the underlying Zettel files change. (major) * Add new startup configuration <tt>default-dir-box-type</tt>, which gives the default value for specifying a directory box type. The default value is “notify”. On macOS, the default value may be changed “simple” if some errors occur while raising the per-process limit of open files. (minor) <a id="0_0_11"></a> <h2>Changes for Version 0.0.11 (2021-04-05)</h2> * New box schema "file" allows to read zettel from a ZIP file. A zettel collection can now be packaged and distributed easier. (major: server) * Non-restricted search is a full-text search. The search string will be normalized according to Unicode NFKD. Every character that is not a letter or a number will be ignored for the search. It is sufficient if the words to be searched are part of words inside a zettel, both content and metadata. (major: api, webui) * A zettel can be excluded from being indexed (and excluded from being found in a search) if it contains the metadata <tt>no-index: true</tt>. (minor: api, webui) * Menu bar is shown when displaying error messages. (minor: webui) * When selecting zettel, it can be specified that a given value should <em>not</em> match. Previously, only the whole select criteria could be negated (which is still possible). (minor: api, webui) * You can select a zettel by specifying that specific metadata keys must (or must not) be present. (minor: api, webui) * Context of a zettel (introduced in version 0.0.10) does not take tags into account any more. Using some tags for determining the context resulted into erratic, non-deterministic context lists. (minor: api, webui) * Selecting zettel depending on tag values can be both by comparing only the prefix or the whole string. If a search value begins with '#', only zettel with the exact tag will be returned. Otherwise a zettel will be returned if the search string just matches the prefix of only one of its tags. (minor: api, webui) * Many smaller bug fixes and improvements, to the software and to the documentation. A note for users of macOS: in the current release and with macOS's default values, a zettel directory must not contain more than approx. 250 files. There are three options to mitigate this limitation temporarily: # You update the per-process limit of open files on macOS. # You setup a virtualization environment to run Zettelstore on Linux or Windows. # You wait for version 0.0.12 which addresses this issue. <a id="0_0_10"></a> <h2>Changes for Version 0.0.10 (2021-02-26)</h2> * Menu item “Home” now redirects to a home zettel. Its default identifier is <tt>000100000000</tt>. The identifier can be changed with configuration key <tt>home-zettel</tt>, which supersedes key <tt>start</tt>. The default home zettel contains some welcoming information for the new user. (major: webui) * Show context of a zettel by following all backward and/or forward reference up to a defined depth and list the resulting zettel. Additionally, some zettel with similar tags as the initial zettel are also taken into account. (major: api, webui) * A zettel that references other zettel within first-level list items, can act as a “table of contents” zettel. The API endpoint <tt>/o/{ID}</tt> allows to retrieve the referenced zettel in the same order as they occur in the zettel. (major: api) * The zettel “New Menu” with identifier <tt>00000000090000</tt> contains a list of all zettel that should act as a template for new zettel. They are listed in the WebUIs ”New“ menu. This is an application of the previous item. It supersedes the usage of a role <tt>new-template</tt> introduced in [#0_0_6|version 0.0.6]. <b>Please update your zettel if you make use of the now deprecated feature.</b> (major: webui) * A reference that starts with two slash characters (“<code>//</code>”) it will be interpreted relative to the value of <code>url-prefix</code>. For example, if <code>url-prefix</code> has the value <code>/manual/</code>, the reference <code>[[Zettel list|//h]]</code> will render as <code><a href="/manual/h">Zettel list</a></code>. (minor: syntax) * Searching/selecting ignores the leading '#' character of tags. (minor: api, webui) * When result of selecting or searching is presented, the query is written as the page heading. (minor: webui) * A reference to a zettel that contains a URL fragment, will now be processed by the indexer. (bug: server) * Runtime configuration key <tt>marker-external</tt> now defaults to “&#10138;” (“➚”). It is more beautiful than the previous “&#8599;&#xfe0e;” (“↗︎”), which also needed the additional “&#xfe0e;” to disable the conversion to an emoji on iPadOS. (minor: webui) * A pre-build binary for macOS ARM64 (also known as Apple silicon) is available. (minor: infrastructure) * Many smaller bug fixes and improvements, to the software and to the documentation. <a id="0_0_9"></a> <h2>Changes for Version 0.0.9 (2021-01-29)</h2> This is the first version that is managed by [https://fossil-scm.org|Fossil] instead of GitHub. To access older versions, use the Git repository under [https://github.com/zettelstore/zettelstore-github|zettelstore-github]. <h3>Server / API</h3> * (major) Support for property metadata. Metadata key <tt>published</tt> is the first example of such a property. * (major) A background activity (called <i>indexer</i>) continuously monitors zettel changes to establish the reverse direction of found internal links. This affects the new metadata keys <tt>precursor</tt> and <tt>folge</tt>. A user specifies the precursor of a zettel and the indexer computes the property metadata for [https://forum.zettelkasten.de/discussion/996/definition-folgezettel|Folgezettel]. Metadata keys with type “Identifier” or “IdentifierSet” that have no inverse key (like <tt>precursor</tt> and <tt>folge</tt> with add to the key <tt>forward</tt> that also collects all internal links within the content. The computed inverse is <tt>backward</tt>, which provides all backlinks. The key <tt>back</tt> is computed as the value of <tt>backward</tt>, but without forward links. Therefore, <tt>back</tt> is something like the list of “smart backlinks”. * (minor) If Zettelstore is being stopped, an appropriate message is written in the console log. * (minor) New computed zettel with environmental data, the list of supported meta data keys, and statistics about all configured zettel boxes. Some other computed zettel got a new identifier (to make room for other variant). * (minor) Remove zettel <tt>00000000000004</tt>, which contained the Go version that produced the Zettelstore executable. It was too specific to the current implementation. This information is now included in zettel <tt>00000000000006</tt> (<i>Zettelstore Environment Values</i>). * (minor) Predefined templates for new zettel do not contain any value for attribute <tt>visibility</tt> any more. * (minor) Add a new metadata key type called “Zettelmarkup”. It is a non-empty string, that will be formatted with Zettelmarkup. <tt>title</tt> and <tt>default-title</tt> have this type. * (major) Rename zettel syntax “meta” to “none”. Please update the <i>Zettelstore Runtime Configuration</i> and all other zettel that previously used the value “meta”. Other zettel are typically user zettel, used for authentication. However, there is no real harm, if you do not update these zettel. In this case, the metadata is just not presented when rendered. Zettelstore will still work. * (minor) Login will take at least 500 milliseconds to mitigate login attacks. This affects both the API and the WebUI. * (minor) Add a sort option “_random” to produce a zettel list in random order. <tt>_order</tt> / <tt>order</tt> are now an aliases for the query parameters <tt>_sort</tt> / <tt>sort</tt>. <h3>WebUI</h3> * (major) HTML template zettel for WebUI now use [https://mustache.github.io/|Mustache] syntax instead of previously used [https://golang.org/pkg/html/template/|Go template] syntax. This allows these zettel to be used, even when there is another Zettelstore implementation, in another programming language. Mustache is available for approx. 48 programming languages, instead of only one for Go templates. <b>If you modified your templates, you <i>must</i> adapt them to the new syntax. Otherwise the WebUI will not work.</b> * (major) Show zettel identifier of folgezettel and precursor zettel in the header of a rendered zettel. If a zettel has real backlinks, they are shown at the botton of the page (“Additional links to this zettel”). * (minor) All property metadata, even computed metadata is shown in the info page of a zettel. * (minor) Rendering of metadata keys <tt>title</tt> and <tt>default-title</tt> in info page changed to a full HTML output for these Zettelmarkup encoded values. * (minor) Always show the zettel identifier on the zettel detail view. Previously, the identifier was not shown if the zettel was not editable. * (minor) Do not show computed metadata in edit forms anymore. <a id="0_0_8"></a> <h2>Changes for Version 0.0.8 (2020-12-23)</h2> <h3>Server / API</h3> * (bug) Zettel files with extension <tt>.jpg</tt> and without metadata will get a <tt>syntax</tt> value “jpg”. The internal data structure got the same value internally, instead of “jpeg”. This has been fixed for all possible alternative syntax values. * (bug) If a file, e.g. an image file like <tt>20201130190200.jpg</tt>, is added to the directory box, its metadata are just calculated from the information available. Updated metadata did not find its way into the zettel box, because the <tt>.meta</tt> file was not written. * (bug) If just the <tt>.meta</tt> file was deleted manually, the zettel was assumed to be missing. A workaround is to restart the software. If the <tt>.meta</tt> file is deleted, metadata is now calculated in the same way when the <tt>.meta</tt> file is non-existing at the start of the software. * (bug) A link to the current zettel, only using a fragment (e.g. <code>[[Title|#title]]</code>) is now handled correctly as a zettel link (and not as a link to external material). * (minor) Allow zettel to be marked as “read only”. This is done through the metadata key <tt>read-only</tt>. * (bug) When renaming a zettel, check all boxes for the new zettel identifier, not just the first one. Otherwise it will be possible to shadow a read-only zettel from a next box, effectively modifying it. * (minor) Add support for a configurable default value for metadata key <tt>visibility</tt>. * (bug) If <tt>list-page-size</tt> is set to a relatively small value and the authenticated user is <i>not</i> the owner, some zettel were not shown in the list of zettel or were not returned by the API. * (minor) Add support for new visibility “expert”. An owner becomes an expert, if the runtime configuration key <tt>expert-mode</tt> is set to true. * (major) Add support for computed zettel. These zettel have an identifier less than <tt>0000000000100</tt>. Most of them are only visible, if <tt>expert-mode</tt> is enabled. * (bug) Fixes a memory leak that results in too many open files after approx. 125 reload operations. * (major) Predefined templates for new zettel got an explicit value for visibility: “login”. Please update these zettel if you modified them. * (major) Rename key <tt>readonly</tt> of <i>Zettelstore Startup Configuration</i> to <tt>read-only-mode</tt>. This was done to avoid some confusion with the the zettel metadata key <tt>read-only</tt>. <b>Please adapt your startup configuration. Otherwise your Zettelstore will be accidentally writable.</b> * (minor) References starting with “./” and “../” are treated as a local reference. Previously, only the prefix “/” was treated as a local reference. * (major) Metadata key <tt>modified</tt> will be set automatically to the current local time if a zettel is updated through Zettelstore. <b>If you used that key previously for your own, you should rename it before you upgrade.</b> * (minor) The new visibility value “simple-expert” ensures that many computed zettel are shown for new users. This is to enable them to send useful bug reports. * (minor) When a zettel is stored as a file, its identifier is additionally stored within the metadata. This helps for better robustness in case the file names were corrupted. In addition, there could be a tool that compares the identifier with the file name. <h3>WebUI</h3> * (minor) Remove list of tags in “List Zettel” and search results. There was some feedback that the additional tags were not helpful. * (minor) Move zettel field "role" above "tags" and move "syntax" more to "content". * (minor) Rename zettel operation “clone” to “copy”. * (major) All predefined HTML templates have now a visibility value “expert”. If you want to see them as an non-expert owner, you must temporary enable <tt>expert-mode</tt> and change the <tt>visibility</tt> metadata value. * (minor) Initial support for [https://zettelkasten.de/posts/tags/folgezettel/|Folgezettel]. If you click on “Folge” (detail view or info view), a new zettel is created with a reference (<tt>precursor</tt>) to the original zettel. Title, role, tags, and syntax are copied from the original zettel. * (major) Most predefined zettel have a title prefix of “Zettelstore”. * (minor) If started in simple mode, e.g. via double click or without any command, some information for the new user is presented. In the terminal, there is a hint about opening the web browser and use |
︙ | ︙ | |||
1277 1278 1279 1280 1281 1282 1283 | to stay with the old licenses (AGPLv3+, CC BY-SA 4.0), you are free to fork from the previous version. <a id="0_0_6"></a> <h2>Changes for Version 0.0.6 (2020-11-23)</h2> <h3>Server</h3> * (major) Rename identifier of <i>Zettelstore Runtime Configuration</i> to | | < | | | | | | | | | | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 | to stay with the old licenses (AGPLv3+, CC BY-SA 4.0), you are free to fork from the previous version. <a id="0_0_6"></a> <h2>Changes for Version 0.0.6 (2020-11-23)</h2> <h3>Server</h3> * (major) Rename identifier of <i>Zettelstore Runtime Configuration</i> to <tt>00000000000100</tt> (previously <tt>00000000000001</tt>). This is done to gain some free identifier with smaller number to be used internally. <b>If you customized this zettel, please make sure to rename it to the new identifier.</b> * (major) Rename the two essential metadata keys of a user zettel to <tt>credential</tt> and <tt>user-id</tt>. The previous values were <tt>cred</tt> and <tt>ident</tt>. <b>If you enabled user authentication and added some user zettel, make sure to change them accordingly. Otherwise these users will not authenticated any more.</b> * (minor) Rename the scheme of the box URL where predefined zettel are stored to “const”. The previous value was “globals”. <h3>Zettelmarkup</h3> * (bug) Allow to specify a <i>fragment</i> in a reference to a zettel. Used to link to an internal position within a zettel. This applies to CommonMark too. <h3>API</h3> * (bug) Encoding binary content in format “json” now results in valid JSON content. * (bug) All query parameters of selecting zettel must be true, regardless if a specific key occurs more than one or not. * (minor) Encode all inherited meta values in all formats except “raw”. A meta value is called <i>inherited</i> if there is a key starting with <tt>default-</tt> in the <i>Zettelstore Runtime Configuration</i>. Applies to WebUI also. * (minor) Automatic calculated identifier for headings (only for “html”, “djson”, “native” format and for the Web user interface). You can use this to provide a zettel reference that links to the heading, without specifying an explicit mark (<code>[!mark]</code>). * (major) Allow to retrieve all references of a given zettel. |
︙ | ︙ | |||
1329 1330 1331 1332 1333 1334 1335 | contrast to “zettel references” and “external references”). When a local reference is displayed as an URL on the WebUI, it will not opened in a new window/tab. They will receive a <i>local</i> marker, when encoded as “djson” or “native”. Local references are listed on the <i>Info page</i> of each zettel. * (minor) Change the default value for some visual sugar put after an | | | | < | | | | | | | | | < | | | | | < | 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 | contrast to “zettel references” and “external references”). When a local reference is displayed as an URL on the WebUI, it will not opened in a new window/tab. They will receive a <i>local</i> marker, when encoded as “djson” or “native”. Local references are listed on the <i>Info page</i> of each zettel. * (minor) Change the default value for some visual sugar put after an external URL to <tt>&\#8599;&\#xfe0e;</tt> (“↗︎”). This affects the former key <tt>icon-material</tt> of the <i>Zettelstore Runtime Configuration</i>, which is renamed to <tt>marker-external</tt>. * (major) Allow multiple zettel to act as templates for creating new zettel. All zettel with a role value “new-template” act as a template to create a new zettel. The WebUI menu item “New” changed to a drop-down list with all those zettel, ordered by their identifier. All metadata keys with the prefix <tt>new-</tt> will be translated to a new or updated keys/value without that prefix. You can use this mechanism to specify a role for the new zettel, or a different title. The title of the template zettel is used in the drop-down list. The initial template zettel “New Zettel” has now a different zettel identifier (now: <tt>00000000091001</tt>, was: <tt>00000000040001</tt>). <b>Please update it, if you changed that zettel.</b> <br>Note: this feature was superseded in [#0_0_10|version 0.0.10] by the “New Menu” zettel. * (minor) When a page should be opened in a new windows (e.g. for external references), the web browser is instructed to decouple the new page from the previous one for privacy and security reasons. In detail, the web browser is instructed to omit referrer information and to omit a JS object linking to the page that contained the external link. * (minor) If the value of the <i>Zettelstore Runtime Configuration</i> key <tt>list-page-size</tt> is greater than zero, the number of WebUI list elements will be restricted and it is possible to change to the next/previous page to list more elements. * (minor) Change CSS to enhance reading: make <code>line-height</code> a little smaller (previous: 1.6, now 1.4) and move list items to the left. <a id="0_0_5"></a> <h2>Changes for Version 0.0.5 (2020-10-22)</h2> * Application Programming Interface (API) to allow external software to retrieve zettel data from the Zettelstore. * Specify boxes, where zettel are stored, via an URL. * Add support for a custom footer. <a id="0_0_4"></a> <h2>Changes for Version 0.0.4 (2020-09-11)</h2> * Optional user authentication/authorization. * New sub-commands <tt>file</tt> (use Zettelstore as a command line filter), <tt>password</tt> (for authentication), and <tt>config</tt>. <a id="0_0_3"></a> <h2>Changes for Version 0.0.3 (2020-08-31)</h2> * Starting Zettelstore has been changed by introducing sub-commands. This change is also reflected on the server installation procedures. * Limitations on renaming zettel has been relaxed. <a id="0_0_2"></a> <h2>Changes for Version 0.0.2 (2020-08-28)</h2> * Configuration zettel now has ID <tt>00000000000001</tt> (previously: <tt>00000000000000</tt>). * The zettel with ID <tt>00000000000000</tt> is no longer shown in any zettel list. If you changed the configuration zettel, you should rename it manually in its file directory. * Creating a new zettel is now done by cloning an existing zettel. To mimic the previous behaviour, a zettel with ID <tt>00000000040001</tt> is introduced. You can change it if you need a different template zettel. <a id="0_0_1"></a> <h2>Changes for Version 0.0.1 (2020-08-21)</h2> * Initial public release. |
Changes to www/download.wiki.
1 2 3 4 5 6 7 8 9 10 11 | <title>Download</title> <h1>Download of Zettelstore Software</h1> <h2>Foreword</h2> * Zettelstore is free/libre open source software, licensed under EUPL-1.2-or-later. * The software is provided as-is. * There is no guarantee that it will not damage your system. * However, it is in use by the main developer since March 2020 without any damage. * It may be useful for you. It is useful for me. * Take a look at the [https://zettelstore.de/manual/|manual] to know how to start and use it. <h2>ZIP-ped Executables</h2> | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <title>Download</title> <h1>Download of Zettelstore Software</h1> <h2>Foreword</h2> * Zettelstore is free/libre open source software, licensed under EUPL-1.2-or-later. * The software is provided as-is. * There is no guarantee that it will not damage your system. * However, it is in use by the main developer since March 2020 without any damage. * It may be useful for you. It is useful for me. * Take a look at the [https://zettelstore.de/manual/|manual] to know how to start and use it. <h2>ZIP-ped Executables</h2> Build: <code>v0.16.0</code> (2023-11-30). * [/uv/zettelstore-0.16.0-linux-amd64.zip|Linux] (amd64) * [/uv/zettelstore-0.16.0-linux-arm.zip|Linux] (arm6, e.g. Raspberry Pi) * [/uv/zettelstore-0.16.0-darwin-arm64.zip|macOS] (arm64) * [/uv/zettelstore-0.16.0-darwin-amd64.zip|macOS] (amd64) * [/uv/zettelstore-0.16.0-windows-amd64.zip|Windows] (amd64) Unzip the appropriate file, install and execute Zettelstore according to the manual. <h2>Zettel for the manual</h2> As a starter, you can download the zettel for the manual [/uv/manual-0.16.0.zip|here]. Just unzip the contained files and put them into your zettel folder or configure a file box to read the zettel directly from the ZIP file. |
Changes to www/index.wiki.
︙ | ︙ | |||
22 23 24 25 26 27 28 | software, which often connects to Zettelstore via its API. Some of the software packages may be experimental. * [https://zettelstore.de/sx|Sx] provides an evaluator for symbolic expressions, which is unsed for HTML templates and more. [https://mastodon.social/tags/Zettelstore|Stay tuned] … <hr> | | | | | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | software, which often connects to Zettelstore via its API. Some of the software packages may be experimental. * [https://zettelstore.de/sx|Sx] provides an evaluator for symbolic expressions, which is unsed for HTML templates and more. [https://mastodon.social/tags/Zettelstore|Stay tuned] … <hr> <h3>Latest Release: 0.16.0 (2023-11-30)</h3> * [./download.wiki|Download] * [./changes.wiki#0_16|Change summary] * [/timeline?p=v0.16.0&bt=v0.15.0&y=ci|Check-ins for version 0.16.0], [/vdiff?to=v0.16.0&from=v0.15.0|content diff] * [/timeline?df=v0.16.0&y=ci|Check-ins derived from the 0.16.0 release], [/vdiff?from=v0.16.0&to=trunk|content diff] * [./plan.wiki|Limitations and planned improvements] * [/timeline?t=release|Timeline of all past releases] <hr> <h2>Build instructions</h2> Just install [https://go.dev/dl/|Go] and some Go-based tools. Please read the [./build.md|instructions] for details. |
︙ | ︙ |
Changes to zettel/content.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettel import ( "bytes" "encoding/base64" "errors" "io" "unicode" "unicode/utf8" "zettelstore.de/z/input" ) // Content is just the content of a zettel. type Content struct { data []byte isBinary bool } |
︙ | ︙ | |||
112 113 114 115 116 117 118 | } // IsBinary returns true if the given data appears to be non-text data. func IsBinary(data []byte) bool { if !utf8.Valid(data) { return true } | | > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | } // IsBinary returns true if the given data appears to be non-text data. func IsBinary(data []byte) bool { if !utf8.Valid(data) { return true } l := len(data) for i := 0; i < l; i++ { if data[i] == 0 { return true } } return false } |
Changes to zettel/content_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package zettel_test import ( "testing" |
︙ | ︙ |
Changes to zettel/id/digraph.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package id import ( "maps" "slices" |
︙ | ︙ |
Changes to zettel/id/digraph_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package id_test import ( "testing" |
︙ | ︙ |
Changes to zettel/id/edge.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 // under this license. //----------------------------------------------------------------------------- package id import "slices" // Edge is a pair of to vertices. |
︙ | ︙ |
Changes to zettel/id/id.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package id provides zettel specific types, constants, and functions about // zettel identifier. package id import ( |
︙ | ︙ | |||
99 100 101 102 103 104 105 | // Only defined for valid ids. func (zid Zid) String() string { var result [14]byte zid.toByteArray(&result) return string(result[:]) } | < < < | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | // Only defined for valid ids. func (zid Zid) String() string { var result [14]byte zid.toByteArray(&result) return string(result[:]) } // Bytes converts the zettel identification to a byte slice of 14 digits. // Only defined for valid ids. func (zid Zid) Bytes() []byte { var result [14]byte zid.toByteArray(&result) return result[:] } |
︙ | ︙ |
Changes to zettel/id/id_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package id_test provides unit tests for testing zettel id specific functions. package id_test import ( "testing" |
︙ | ︙ | |||
71 72 73 74 75 76 77 | } } var sResult string // to disable compiler optimization in loop below func BenchmarkString(b *testing.B) { var s string | | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | } } var sResult string // to disable compiler optimization in loop below func BenchmarkString(b *testing.B) { var s string for n := 0; n < b.N; n++ { s = id.Zid(12345678901200).String() } sResult = s } var bResult []byte // to disable compiler optimization in loop below func BenchmarkBytes(b *testing.B) { var bs []byte for n := 0; n < b.N; n++ { bs = id.Zid(12345678901200).Bytes() } bResult = bs } |
Changes to zettel/id/set.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package id import ( "maps" "strings" |
︙ | ︙ |
Changes to zettel/id/set_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package id_test import ( "testing" |
︙ | ︙ | |||
136 137 138 139 140 141 142 | 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{} | | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 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)] = struct{}{} } } |
Changes to zettel/id/slice.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package id import ( "slices" "strings" |
︙ | ︙ |
Changes to zettel/id/slice_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 // under this license. //----------------------------------------------------------------------------- package id_test import ( "testing" |
︙ | ︙ |
Changes to zettel/meta/collection.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // under this license. //----------------------------------------------------------------------------- package meta import "sort" // Arrangement stores metadata within its categories. |
︙ | ︙ |
Changes to zettel/meta/meta.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package meta provides the zettel specific type 'meta'. package meta import ( "regexp" "sort" "strings" "unicode" "unicode/utf8" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/maps" "zettelstore.de/z/input" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" ) type keyUsage int const ( |
︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 | // IsComputed returns true, if metadata is computed and not set by the user. func (kd *DescriptionKey) IsComputed() bool { return kd.usage >= usageComputed } // IsProperty returns true, if metadata is a computed property. func (kd *DescriptionKey) IsProperty() bool { return kd.usage >= usageProperty } var registeredKeys = make(map[string]*DescriptionKey) func registerKey(name string, t *DescriptionType, usage keyUsage, inverse string) { if _, ok := registeredKeys[name]; ok { panic("Key '" + name + "' already defined") } if inverse != "" { | > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | // IsComputed returns true, if metadata is computed and not set by the user. func (kd *DescriptionKey) IsComputed() bool { return kd.usage >= usageComputed } // IsProperty returns true, if metadata is a computed property. func (kd *DescriptionKey) IsProperty() bool { return kd.usage >= usageProperty } // IsStoredComputed retruns true, if metadata is computed, but also stored. func (kd *DescriptionKey) IsStoredComputed() bool { return kd.usage == usageComputed } var registeredKeys = make(map[string]*DescriptionKey) func registerKey(name string, t *DescriptionType, usage keyUsage, inverse string) { if _, ok := registeredKeys[name]; ok { panic("Key '" + name + "' already defined") } if inverse != "" { |
︙ | ︙ |
Changes to zettel/meta/meta_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta import ( "strings" "testing" |
︙ | ︙ | |||
230 231 232 233 234 235 236 | if m2.Equal(m1, true) { t.Error("Different ID should differentiate") } } func pairs2meta(pairs []string) *Meta { m := New(testID) | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | if m2.Equal(m1, true) { t.Error("Different ID should differentiate") } } func pairs2meta(pairs []string) *Meta { m := New(testID) for i := 0; i < len(pairs); i = i + 2 { m.Set(pairs[i], pairs[i+1]) } return m } func TestRemoveNonGraphic(t *testing.T) { testCases := []struct { |
︙ | ︙ |
Changes to zettel/meta/parse.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta import ( "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/maps" "zettelstore.de/z/input" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" ) // NewFromInput parses the meta data of a zettel. func NewFromInput(zid id.Zid, inp *input.Input) *Meta { if inp.Ch == '-' && inp.PeekN(0) == '-' && inp.PeekN(1) == '-' { |
︙ | ︙ | |||
155 156 157 158 159 160 161 162 163 164 165 166 167 168 | } switch Type(key) { case TypeTagSet: addSet(m, key, strings.ToLower(v), func(s string) bool { return s[0] == '#' && len(s) > 1 }) case TypeWord: m.Set(key, strings.ToLower(v)) case TypeID: if _, err := id.Parse(v); err == nil { m.Set(key, v) } case TypeIDSet: addSet(m, key, v, func(s string) bool { _, err := id.Parse(s) | > > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | } switch Type(key) { case TypeTagSet: addSet(m, key, strings.ToLower(v), func(s string) bool { return s[0] == '#' && len(s) > 1 }) case TypeWord: m.Set(key, strings.ToLower(v)) case TypeWordSet: addSet(m, key, strings.ToLower(v), func(s string) bool { return true }) case TypeID: if _, err := id.Parse(v); err == nil { m.Set(key, v) } case TypeIDSet: addSet(m, key, v, func(s string) bool { _, err := id.Parse(s) |
︙ | ︙ |
Changes to zettel/meta/parse_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta_test import ( "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/z/input" "zettelstore.de/z/zettel/meta" ) func parseMetaStr(src string) *meta.Meta { return meta.NewFromInput(testID, input.NewInput([]byte(src))) } |
︙ | ︙ | |||
136 137 138 139 140 141 142 | } } func equalPairs(one, two []meta.Pair) bool { if len(one) != len(two) { return false } | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | } } func equalPairs(one, two []meta.Pair) bool { if len(one) != len(two) { return false } for i := 0; i < len(one); i++ { if one[i].Key != two[i].Key || one[i].Value != two[i].Value { return false } } return true } |
︙ | ︙ |
Changes to zettel/meta/type.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta import ( "strconv" "strings" |
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | TypeIDSet = registerType(api.MetaIDSet, true) TypeNumber = registerType(api.MetaNumber, false) TypeString = registerType(api.MetaString, false) TypeTagSet = registerType(api.MetaTagSet, true) TypeTimestamp = registerType(api.MetaTimestamp, false) TypeURL = registerType(api.MetaURL, false) TypeWord = registerType(api.MetaWord, false) TypeZettelmarkup = registerType(api.MetaZettelmarkup, false) ) // Type returns a type hint for the given key. If no type hint is specified, // TypeUnknown is returned. func (*Meta) Type(key string) *DescriptionType { return Type(key) } | > < < < < < < | | | > | | | | | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | TypeIDSet = registerType(api.MetaIDSet, true) TypeNumber = registerType(api.MetaNumber, false) TypeString = registerType(api.MetaString, false) TypeTagSet = registerType(api.MetaTagSet, true) TypeTimestamp = registerType(api.MetaTimestamp, false) TypeURL = registerType(api.MetaURL, false) TypeWord = registerType(api.MetaWord, false) TypeWordSet = registerType(api.MetaWordSet, true) TypeZettelmarkup = registerType(api.MetaZettelmarkup, false) ) // Type returns a type hint for the given key. If no type hint is specified, // TypeUnknown is returned. func (*Meta) Type(key string) *DescriptionType { return Type(key) } var ( cachedTypedKeys = make(map[string]*DescriptionType) mxTypedKey sync.RWMutex suffixTypes = map[string]*DescriptionType{ "-date": TypeTimestamp, "-number": TypeNumber, "-role": TypeWord, "-set": TypeWordSet, "-time": TypeTimestamp, "-title": TypeZettelmarkup, "-url": TypeURL, "-zettel": TypeID, "-zid": TypeID, "-zids": TypeIDSet, } ) // Type returns a type hint for the given key. If no type hint is specified, // TypeEmpty is returned. func Type(key string) *DescriptionType { if k, ok := registeredKeys[key]; ok { |
︙ | ︙ |
Changes to zettel/meta/type_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta_test import ( "strconv" "testing" |
︙ | ︙ |
Changes to zettel/meta/values.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta import ( "fmt" |
︙ | ︙ | |||
34 35 36 37 38 39 40 | SyntaxPNG = "png" SyntaxSVG = api.ValueSyntaxSVG SyntaxSxn = api.ValueSyntaxSxn SyntaxText = api.ValueSyntaxText SyntaxTxt = "txt" SyntaxWebp = "webp" SyntaxZmk = api.ValueSyntaxZmk | < < | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | SyntaxPNG = "png" SyntaxSVG = api.ValueSyntaxSVG SyntaxSxn = api.ValueSyntaxSxn SyntaxText = api.ValueSyntaxText SyntaxTxt = "txt" SyntaxWebp = "webp" SyntaxZmk = api.ValueSyntaxZmk ) // Visibility enumerates the variations of the 'visibility' meta key. type Visibility int // Supported values for visibility. const ( |
︙ | ︙ |
Changes to zettel/meta/write.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta import "io" // Write writes metadata to a writer, excluding computed and propery values. |
︙ | ︙ |
Changes to zettel/meta/write_test.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- package meta_test import ( "strings" "testing" |
︙ | ︙ |
Changes to zettel/zettel.go.
1 2 3 4 5 6 7 8 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 // under this license. //----------------------------------------------------------------------------- // Package zettel provides specific types, constants, and functions for zettel. package zettel import "zettelstore.de/z/zettel/meta" |
︙ | ︙ |