about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-05-06 10:14:17 +0200
committerAlan Pearce2024-05-06 10:14:17 +0200
commitc0fbf11f843af84e8891a708c4d217dd6c523473 (patch)
tree1fca048be6f784dd895d2c2d9ff09200d5d610fd
parent7d08b696e9ab6de61c53a5dc9153595a5a8a6d98 (diff)
downloadsearchix-c0fbf11f843af84e8891a708c4d217dd6c523473.tar.lz
searchix-c0fbf11f843af84e8891a708c4d217dd6c523473.tar.zst
searchix-c0fbf11f843af84e8891a708c4d217dd6c523473.zip
feat: render markdown examples
-rw-r--r--frontend/templates/blocks/options.gotmpl18
-rw-r--r--internal/options/option.go62
-rw-r--r--internal/options/process.go78
-rw-r--r--internal/server/server.go19
4 files changed, 109 insertions, 68 deletions
diff --git a/frontend/templates/blocks/options.gotmpl b/frontend/templates/blocks/options.gotmpl
index 89c6f33..a4c4f12 100644
--- a/frontend/templates/blocks/options.gotmpl
+++ b/frontend/templates/blocks/options.gotmpl
@@ -5,7 +5,7 @@
         {{ .Option }}
       </summary>
       <p>
-        {{ HTML .Description.HTML }}
+        {{ markdown .Description }}
       </p>
       <dl>
         {{- with .Type }}
@@ -15,18 +15,26 @@
         {{- with .Default }}
           <dt>Default</dt>
           <dd>
-            <pre><code>{{ .Text }}</code></pre>
+            {{- if .Markdown }}
+              {{ .Markdown }}
+            {{- else }}
+              <pre><code>{{ .Text }}</code></pre>
+            {{- end }}
           </dd>
         {{- end }}
         {{- with .Example }}
-          {{- if .Text }}
+          {{- if or .Text .Markdown }}
             <dt>Example</dt>
             <dd>
-              <pre><code>{{ .Text }}</code></pre>
+              {{- if .Markdown }}
+                {{ .Markdown }}
+              {{- else }}
+                <pre><code>{{ .Text }}</code></pre>
+              {{- end }}
             </dd>
           {{- end }}
         {{- end }}
-        {{- with .RelatedPackages.HTML }}
+        {{- with .RelatedPackages }}
           <dt>Related Packages</dt>
           <dd>{{ . }}</dd>
         {{- end }}
diff --git a/internal/options/option.go b/internal/options/option.go
index b8b838a..a43dd49 100644
--- a/internal/options/option.go
+++ b/internal/options/option.go
@@ -1,67 +1,27 @@
 package options
 
-import (
-	"encoding/json"
-	"strings"
-
-	"github.com/pkg/errors"
-	"github.com/yuin/goldmark"
-	"github.com/yuin/goldmark/extension"
-)
+type Markdown string
 
 type NixValue struct {
-	Type string `json:"_type" mapstructure:"_type"`
-	Text string `json:"text"`
-}
-
-type HTML struct {
-	HTML string
-}
-
-var md = goldmark.New(
-	goldmark.WithExtensions(extension.NewLinkify()),
-)
-
-func (html *HTML) UnmarshalText(text []byte) error {
-	var out strings.Builder
-	err := md.Convert(text, &out)
-	if err != nil {
-		return errors.WithMessage(err, "failed to convert markdown to HTML")
-	}
-
-	html.HTML = out.String()
-
-	return nil
-}
-
-func (html *HTML) UnmarshalJSON(raw []byte) error {
-	var v struct {
-		HTML string
-	}
-	err := json.Unmarshal(raw, &v)
-	if err != nil {
-		return errors.WithMessage(err, "error unmarshaling json")
-	}
-	html.HTML = v.HTML
-
-	return nil
+	Text     string   `json:",omitempty"`
+	Markdown Markdown `json:",omitempty"`
 }
 
 type Link struct {
 	Name string
-	URL  string `json:"url"`
+	URL  string
 }
 
 type NixOption struct {
-	Option          string
+	Option string
+
 	Declarations    []Link
-	Default         NixValue
-	Description     HTML
-	Example         NixValue
-	ReadOnly        bool
-	Type            string
+	Default         *NixValue `json:",omitempty"`
+	Description     Markdown
+	Example         *NixValue `json:",omitempty"`
 	Loc             []string
-	RelatedPackages HTML
+	RelatedPackages Markdown `json:",omitempty"`
+	Type            string
 }
 
 type NixOptions []NixOption
diff --git a/internal/options/process.go b/internal/options/process.go
index fde73e4..9721a7b 100644
--- a/internal/options/process.go
+++ b/internal/options/process.go
@@ -3,6 +3,7 @@ package options
 import (
 	"encoding/json"
 	"io"
+	"log/slog"
 	"os"
 
 	"github.com/bcicen/jstream"
@@ -10,6 +11,27 @@ import (
 	"github.com/pkg/errors"
 )
 
+type nixValueJSON struct {
+	Type string `mapstructure:"_type"`
+	Text string
+}
+
+type linkJSON struct {
+	Name string
+	URL  string `json:"url"`
+}
+
+type nixOptionJSON struct {
+	Declarations    []linkJSON
+	Default         nixValueJSON
+	Description     string
+	Example         nixValueJSON
+	Loc             []string
+	ReadOnly        bool
+	RelatedPackages string
+	Type            string
+}
+
 func ValueTypeToString(valueType jstream.ValueType) string {
 	switch valueType {
 	case jstream.Unknown:
@@ -31,6 +53,27 @@ func ValueTypeToString(valueType jstream.ValueType) string {
 	return "very strange"
 }
 
+func convertNixValue(nj nixValueJSON) *NixValue {
+	switch nj.Type {
+	case "", "literalExpression":
+		if nj.Text == "" {
+			return nil
+		}
+
+		return &NixValue{
+			Text: nj.Text,
+		}
+	case "literalMD":
+		return &NixValue{
+			Markdown: Markdown(nj.Text),
+		}
+	default:
+		slog.Warn("got unexpected NixValue type", "type", nj.Type, "text", nj.Text)
+
+		return nil
+	}
+}
+
 func Process(inpath string, outpath string) error {
 	infile, err := os.Open(inpath)
 	if err != nil {
@@ -46,10 +89,11 @@ func Process(inpath string, outpath string) error {
 	}
 
 	dec := jstream.NewDecoder(infile, 1).EmitKV()
-	var opt NixOption
+	var optJSON nixOptionJSON
 	ms, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
 		ErrorUnused: true,
-		Result:      &opt,
+		ZeroFields:  true,
+		Result:      &optJSON,
 		Squash:      true,
 		DecodeHook:  mapstructure.TextUnmarshallerHookFunc(),
 	})
@@ -69,17 +113,29 @@ func Process(inpath string, outpath string) error {
 			return errors.Errorf("unexpected object type %s", ValueTypeToString(mv.ValueType))
 		}
 		kv := mv.Value.(jstream.KV)
-		if kv.Key == "_module.args" {
-			continue
-		}
 		x := kv.Value.(map[string]interface{})
-		x["option"] = kv.Key
 
-		err = ms.Decode(x)
+		err = ms.Decode(x) // stores in optJSON
 		if err != nil {
 			return errors.WithMessagef(err, "failed to decode option %#v", x)
 		}
 
+		var decs = make([]Link, len(optJSON.Declarations))
+		for i, d := range optJSON.Declarations {
+			decs[i] = Link(d)
+		}
+
+		opt := NixOption{
+			Option:          kv.Key,
+			Declarations:    decs,
+			Default:         convertNixValue(optJSON.Default),
+			Description:     Markdown(optJSON.Description),
+			Example:         convertNixValue(optJSON.Example),
+			RelatedPackages: Markdown(optJSON.RelatedPackages),
+			Loc:             optJSON.Loc,
+			Type:            optJSON.Type,
+		}
+
 		b, err := json.MarshalIndent(opt, "", "  ")
 		if err != nil {
 			return errors.WithMessagef(err, "failed to encode option %#v", opt)
@@ -95,9 +151,11 @@ func Process(inpath string, outpath string) error {
 		}
 	}
 
-	_, err = outfile.Seek(-2, io.SeekCurrent)
-	if err != nil {
-		return errors.WithMessage(err, "could not write to output")
+	if outpath != "/dev/stdout" {
+		_, err = outfile.Seek(-2, io.SeekCurrent)
+		if err != nil {
+			return errors.WithMessage(err, "could not write to output")
+		}
 	}
 
 	_, err = outfile.WriteString("]\n")
diff --git a/internal/server/server.go b/internal/server/server.go
index ad75ac1..76fbecb 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"html"
 	"html/template"
 	"io"
 	"log"
@@ -25,6 +26,8 @@ import (
 	"github.com/osdevisnot/sorvor/pkg/livereload"
 	"github.com/pkg/errors"
 	"github.com/shengyanli1982/law"
+	"github.com/yuin/goldmark"
+	"github.com/yuin/goldmark/extension"
 )
 
 var config *cfg.Config
@@ -74,9 +77,20 @@ func applyDevModeOverrides(config *cfg.Config) {
 	config.CSP.ConnectSrc = slices.Insert(config.CSP.ConnectSrc, 0, "'self'")
 }
 
+var md = goldmark.New(
+	goldmark.WithExtensions(extension.NewLinkify()),
+)
 var templateFuncs = template.FuncMap{
-	"HTML": func(input string) template.HTML {
-		return template.HTML(input) // #nosec G203
+	"markdown": func(input options.Markdown) template.HTML {
+		var out strings.Builder
+		err := md.Convert([]byte(input), &out)
+		if err != nil {
+			slog.Warn(fmt.Sprintf("markdown conversion failed: %v", err))
+
+			return template.HTML(html.EscapeString(err.Error())) // #nosec G203 -- duh?
+		}
+
+		return template.HTML(out.String()) // #nosec G203
 	},
 }
 
@@ -177,6 +191,7 @@ func New(runtimeConfig *Config) (*Server, error) {
 	if err != nil {
 		slog.Error(fmt.Sprintf("error parsing json file: %v", err))
 	}
+
 	mux.HandleFunc("/options/results", func(w http.ResponseWriter, r *http.Request) {
 		tdata := OptionResultData{
 			TemplateData: indexData,