Index: api/api.go ================================================================== --- api/api.go +++ api/api.go @@ -97,10 +97,11 @@ } // ZettelListJSON contains data for a zettel list. type ZettelListJSON struct { Query string `json:"query"` + Human string `json:"human"` List []ZidMetaJSON `json:"list"` } // MapMeta maps metadata keys to list of metadata. type MapMeta map[string][]ZettelID Index: api/urlbuilder.go ================================================================== --- api/urlbuilder.go +++ api/urlbuilder.go @@ -86,10 +86,17 @@ func (ub *URLBuilder) AppendQuery(key, value string) *URLBuilder { ub.rawLocal = "" ub.query = append(ub.query, urlQuery{key, value}) return ub } + +// AppendSearch adds a new search +func (ub *URLBuilder) AppendSearch(value string) *URLBuilder { + ub.rawLocal = "" + ub.query = append(ub.query, urlQuery{QueryKeySearch, value}) + return ub +} // ClearQuery removes all query parameters. func (ub *URLBuilder) ClearQuery() *URLBuilder { ub.rawLocal = "" ub.query = nil Index: client/client.go ================================================================== --- client/client.go +++ client/client.go @@ -286,27 +286,27 @@ } return lines, nil } // ListZettelJSON returns a list of zettel. -func (c *Client) ListZettelJSON(ctx context.Context, query url.Values) (string, []api.ZidMetaJSON, error) { +func (c *Client) ListZettelJSON(ctx context.Context, query url.Values) (string, string, []api.ZidMetaJSON, error) { ub := c.newQueryURLBuilder('j', query) resp, err := c.buildAndExecuteRequest(ctx, http.MethodGet, ub, nil, nil) if err != nil { - return "", nil, err + return "", "", nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return "", nil, statusToError(resp) + return "", "", nil, statusToError(resp) } dec := json.NewDecoder(resp.Body) var zl api.ZettelListJSON err = dec.Decode(&zl) if err != nil { - return "", nil, err + return "", "", nil, err } - return zl.Query, zl.List, nil + return zl.Query, zl.Human, zl.List, nil } // GetZettel returns a zettel as a string. func (c *Client) GetZettel(ctx context.Context, zid api.ZettelID, part string) ([]byte, error) { ub := c.newURLBuilder('z').SetZid(zid) Index: go.mod ================================================================== --- go.mod +++ go.mod @@ -1,5 +1,5 @@ module zettelstore.de/c -go 1.18 +go 1.19 require codeberg.org/t73fde/sxpf v0.0.0-20220719090054-749a39d0a7a0 Index: html/html.go ================================================================== --- html/html.go +++ html/html.go @@ -31,15 +31,12 @@ `<`, htmlLt, `>`, htmlGt, `"`, htmlQuot, "\000", htmlNull, } - htmlEscaper = strings.NewReplacer(htmlEscapes...) - htmlLitEscapes = append(append([]string{}, htmlEscapes...), - " ", htmlLitSpace, - ) - htmlLitEscaper = strings.NewReplacer(htmlLitEscapes...) + htmlEscaper = strings.NewReplacer(htmlEscapes...) + htmlVisEscapes = append(append([]string{}, htmlEscapes...), " ", htmlVisSpace, htmlLitSpace, htmlVisSpace, ) htmlVisEscaper = strings.NewReplacer(htmlVisEscapes...) @@ -46,14 +43,10 @@ ) // Escape writes to w the escaped HTML equivalent of the given string. func Escape(w io.Writer, s string) (int, error) { return htmlEscaper.WriteString(w, s) } -// EscapeLiteral writes the escaped HTML equivaltent of the given string. -// Each space character is written as U+00A0. -func EscapeLiteral(w io.Writer, s string) (int, error) { return htmlLitEscaper.WriteString(w, s) } - // EscapeVisible writes to w the escaped HTML equivalent of the given string. // Each space is written as U-2423. func EscapeVisible(w io.Writer, s string) (int, error) { return htmlVisEscaper.WriteString(w, s) } var ( Index: html/html_test.go ================================================================== --- html/html_test.go +++ html/html_test.go @@ -26,30 +26,10 @@ } for _, tc := range testcases { var buf bytes.Buffer _, err := html.Escape(&buf, tc.in) if err != nil { - t.Errorf("Escape(%q) got error: %v", tc.in, err) - } - if got := buf.String(); tc.exp != got { - t.Errorf("Escape(%q) == %q, but got %q", tc.in, tc.exp, got) - } - } -} - -func TestEscapeLiteral(t *testing.T) { - testcases := []struct { - in, exp string - }{ - {"", ""}, - {"<", "<"}, - {" a b ", "\u00a0a\u00a0\u00a0b\u00a0"}, - } - for _, tc := range testcases { - var buf bytes.Buffer - _, err := html.EscapeLiteral(&buf, tc.in) - if err != nil { t.Errorf("Escape(%q) got error: %v", tc.in, err) } if got := buf.String(); tc.exp != got { t.Errorf("Escape(%q) == %q, but got %q", tc.in, tc.exp, got) } Index: html/sencoder.go ================================================================== --- html/sencoder.go +++ html/sencoder.go @@ -13,10 +13,11 @@ import ( "bytes" "fmt" "io" "log" + "net/url" "strconv" "codeberg.org/t73fde/sxpf" "zettelstore.de/c/api" "zettelstore.de/c/attrs" @@ -133,16 +134,16 @@ if env.err == nil { _, env.err = Escape(env.w, s) } } -func (env *EncEnvironment) WriteEscapedLiteral(s string) { +func (env *EncEnvironment) WriteEscapedOrVisible(s string) { if env.err == nil { if env.visibleSpace { _, env.err = EscapeVisible(env.w, s) } else { - _, env.err = EscapeLiteral(env.w, s) + _, env.err = Escape(env.w, s) } } } func (env *EncEnvironment) GetSymbol(p *sxpf.Pair) (res *sxpf.Symbol) { @@ -498,10 +499,16 @@ WriteLink(env, args, a.AddClass("broken"), refValue, "") } }}, {sexpr.SymLinkHosted, 2, func(env *EncEnvironment, args *sxpf.Pair) { WriteHRefLink(env, args) }}, {sexpr.SymLinkBased, 2, func(env *EncEnvironment, args *sxpf.Pair) { WriteHRefLink(env, args) }}, + {sexpr.SymLinkSearch, 2, func(env *EncEnvironment, args *sxpf.Pair) { + if a, refValue, ok := PrepareLink(env, args); ok { + query := "?" + api.QueryKeySearch + "=" + url.QueryEscape(refValue) + WriteLink(env, args, a.Set("href", query), refValue, "") + } + }}, {sexpr.SymLinkExternal, 2, func(env *EncEnvironment, args *sxpf.Pair) { if a, refValue, ok := PrepareLink(env, args); ok { WriteLink(env, args, a.Set("href", refValue).AddClass("external"), refValue, "") } }}, @@ -562,11 +569,11 @@ } }}, {sexpr.SymFormatDelete, 1, makeFormatFn("del")}, {sexpr.SymFormatEmph, 1, makeFormatFn("em")}, {sexpr.SymFormatInsert, 1, makeFormatFn("ins")}, - {sexpr.SymFormatQuote, 1, makeFormatFn("q")}, + {sexpr.SymFormatQuote, 1, writeQuote}, {sexpr.SymFormatSpan, 1, makeFormatFn("span")}, {sexpr.SymFormatStrong, 1, makeFormatFn("strong")}, {sexpr.SymFormatSub, 1, makeFormatFn("sub")}, {sexpr.SymFormatSuper, 1, makeFormatFn("sup")}, {sexpr.SymLiteralComment, 1, func(env *EncEnvironment, args *sxpf.Pair) { @@ -647,11 +654,11 @@ } func (env *EncEnvironment) writeVerbatim(args *sxpf.Pair, a attrs.Attributes) { env.WriteString("
")
 	env.WriteStartTag("code", a)
-	env.WriteEscapedLiteral(env.GetString(args.GetTail()))
+	env.WriteEscapedOrVisible(env.GetString(args.GetTail()))
 	env.WriteString("
") } func execHTML(env *EncEnvironment, args *sxpf.Pair) { if s := env.GetString(args.GetTail()); s != "" && IsSafe(s) { @@ -722,10 +729,26 @@ env.WriteStartTag(tag, a) sxpf.EvalSequence(env, args.GetTail()) env.WriteEndTag(tag) } } + +func writeQuote(env *EncEnvironment, args *sxpf.Pair) { + const langAttr = "lang" + a := env.GetAttributes(args) + lang, hasLang := a.Get(langAttr) + if hasLang { + a = a.Remove(langAttr) + env.WriteStartTag("span", attrs.Attributes{}.Set(langAttr, lang)) + } + env.WriteStartTag("q", a) + sxpf.EvalSequence(env, args.GetTail()) + env.WriteEndTag("q") + if hasLang { + env.WriteEndTag("span") + } +} func (env *EncEnvironment) writeLiteral(args *sxpf.Pair, a attrs.Attributes, tag string) { if a == nil { a = env.GetAttributes(args) } @@ -733,11 +756,11 @@ if a.HasDefault() { env.visibleSpace = true a = a.RemoveDefault() } env.WriteStartTag(tag, a) - env.WriteEscapedLiteral(env.GetString(args.GetTail())) + env.WriteEscapedOrVisible(env.GetString(args.GetTail())) env.visibleSpace = oldVisible env.WriteEndTag(tag) } func setProgLang(a attrs.Attributes) attrs.Attributes { Index: sexpr/const.go ================================================================== --- sexpr/const.go +++ sexpr/const.go @@ -42,10 +42,11 @@ SymLinkSelf = Smk.MakeSymbol("LINK-SELF") SymLinkFound = Smk.MakeSymbol("LINK-FOUND") SymLinkBroken = Smk.MakeSymbol("LINK-BROKEN") SymLinkHosted = Smk.MakeSymbol("LINK-HOSTED") SymLinkBased = Smk.MakeSymbol("LINK-BASED") + SymLinkSearch = Smk.MakeSymbol("LINK-SEARCH") SymLinkExternal = Smk.MakeSymbol("LINK-EXTERNAL") SymListOrdered = Smk.MakeSymbol("ORDERED") SymListUnordered = Smk.MakeSymbol("UNORDERED") SymListQuote = Smk.MakeSymbol("QUOTATION") SymLiteralProg = Smk.MakeSymbol("LITERAL-CODE") @@ -83,10 +84,11 @@ SymRefStateSelf = Smk.MakeSymbol("SELF") SymRefStateFound = Smk.MakeSymbol("FOUND") SymRefStateBroken = Smk.MakeSymbol("BROKEN") SymRefStateHosted = Smk.MakeSymbol("HOSTED") SymRefStateBased = Smk.MakeSymbol("BASED") + SymRefStateSearch = Smk.MakeSymbol("SEARCH") SymRefStateExternal = Smk.MakeSymbol("EXTERNAL") ) // Symbols for metadata types var ( ADDED www/changes.wiki Index: www/changes.wiki ================================================================== --- www/changes.wiki +++ www/changes.wiki @@ -0,0 +1,15 @@ +Change Log + + +

Changes for Version 0.6.0 (2022-08-11)

+ * Add support to build URLs with search expressions + * Use Go 1.19 + * Fix some bugs + + +

Changes for Version 0.5.1 (2022-08-08)

+ * Support for search references + (minor: api, zjson, sexpr, html) + +

Changes for Version 0.5 (2022-07-29)

+ * Initial public release. Index: www/index.wiki ================================================================== --- www/index.wiki +++ www/index.wiki @@ -1,12 +1,12 @@ Home This repository contains Go client software to access [https://zettelstore.de|Zettelstore] via its API. -

Latest Release: 0.5 (2022-07-29)

- * [./changes.wiki#0_5|Change summary] - * [/timeline?p=v0.5&bt=v0.4&y=ci|Check-ins for version 0.5], - [/vdiff?to=v0.5&from=v0.4|content diff] - * [/timeline?df=v0.5&y=ci|Check-ins derived from the 0.5 release], - [/vdiff?from=v0.5&to=trunk|content diff] +

Latest Release: 0.6.0 (2022-08-11)

+ * [./changes.wiki#0_6|Change summary] + * [/timeline?p=v0.6.0&bt=v0.5&y=ci|Check-ins for version 0.6.0], + [/vdiff?to=v0.6.0&from=v0.5|content diff] + * [/timeline?df=v0.6.0&y=ci|Check-ins derived from the 0.6.0 release], + [/vdiff?from=v0.6.0&to=trunk|content diff] * [/timeline?t=release|Timeline of all past releases] Index: zjson/const.go ================================================================== --- zjson/const.go +++ zjson/const.go @@ -84,10 +84,11 @@ RefStateBroken = "broken" RefStateExternal = "external" RefStateFound = "found" RefStateHosted = "local" RefStateInvalid = "invalid" + RefStateSearch = "search" RefStateSelf = "self" RefStateZettel = "zettel" ) // Values for table cell alignment