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
==================================================================
--- /dev/null
+++ www/changes.wiki
@@ -0,0 +1,15 @@
+