From a61bcdeb314d4e0e9f6e8915b92010895170e785 Mon Sep 17 00:00:00 2001 From: tjpcc Date: Fri, 1 Sep 2023 12:37:42 -0600 Subject: refactor gophermap template handling and add markdown conversion --- gopher/gophermap/htmlconv/convert.go | 135 +--------------------------- gopher/gophermap/internal/templates.go | 153 ++++++++++++++++++++++++++++++++ gopher/gophermap/mdconv/convert.go | 52 +++++++++++ gopher/gophermap/mdconv/convert_test.go | 69 ++++++++++++++ 4 files changed, 278 insertions(+), 131 deletions(-) create mode 100644 gopher/gophermap/internal/templates.go create mode 100644 gopher/gophermap/mdconv/convert.go create mode 100644 gopher/gophermap/mdconv/convert_test.go (limited to 'gopher') diff --git a/gopher/gophermap/htmlconv/convert.go b/gopher/gophermap/htmlconv/convert.go index a669601..8fa2f52 100644 --- a/gopher/gophermap/htmlconv/convert.go +++ b/gopher/gophermap/htmlconv/convert.go @@ -1,13 +1,11 @@ package htmlconv import ( - "bytes" "html/template" "io" - "strings" "tildegit.org/tjp/sliderule/gopher" - "tildegit.org/tjp/sliderule/internal/types" + "tildegit.org/tjp/sliderule/gopher/gophermap/internal" ) // Convert writes html to a writer from the provided gophermap document. @@ -32,13 +30,13 @@ func Convert(wr io.Writer, doc gopher.MapDocument, overrides *template.Template) return err } - tmpl, err = addOverrides(tmpl, overrides) + tmpl, err = internal.AddHTMLOverrides(tmpl, overrides) if err != nil { return err } - for _, item := range renderItems(doc) { - if err := tmpl.ExecuteTemplate(wr, item.template, item.object); err != nil { + for _, item := range internal.RenderItems(doc) { + if err := tmpl.ExecuteTemplate(wr, item.Template, item.Object); err != nil { return err } } @@ -46,131 +44,6 @@ func Convert(wr io.Writer, doc gopher.MapDocument, overrides *template.Template) return nil } -type renderItem struct { - template string - object any -} - -type renderRef struct { - Type types.Status - Display string - Selector template.URL - Hostname string - Port string -} - -func refItem(item gopher.MapItem) renderRef { - return renderRef{ - Type: item.Type, - Display: item.Display, - Selector: template.URL(item.Selector), - Hostname: item.Hostname, - Port: item.Port, - } -} - -func renderItems(doc gopher.MapDocument) []renderItem { - out := make([]renderItem, 0, len(doc)) - out = append(out, renderItem{ - template: "header", - object: doc, - }) - inMsg := false - msg := &bytes.Buffer{} - var currentHost, currentPort string - - for _, mapItem := range doc { - switch mapItem.Type { - case gopher.InfoMessageType: - if inMsg { - _, _ = msg.WriteString("\n") - } else { - msg.Reset() - } - _, _ = msg.WriteString(mapItem.Display) - inMsg = true - - if currentHost == "" { - currentHost = mapItem.Hostname - } - if currentPort == "" { - currentPort = mapItem.Port - } - case gopher.GifFileType, gopher.ImageFileType, gopher.BitmapType, gopher.PngImageFileType: - if inMsg { - out = append(out, renderItem{ - template: "message", - object: msg.String(), - }) - inMsg = false - } - out = append(out, renderItem{ - template: "image", - object: refItem(mapItem), - }) - default: - if inMsg { - out = append(out, renderItem{ - template: "message", - object: msg.String(), - }) - inMsg = false - } - out = append(out, renderItem{ - template: "link", - object: refItem(mapItem), - }) - } - } - - if inMsg { - out = append(out, renderItem{ - template: "message", - object: msg.String(), - }) - } - - simplifyLinks(out, currentHost, currentPort) - - return append(out, renderItem{ - template: "footer", - object: doc, - }) -} - -func simplifyLinks(items []renderItem, currentHost, currentPort string) { - for i, item := range items { - if item.template != "link" && item.template != "image" { - continue - } - - m := item.object.(renderRef) - if m.Hostname == currentHost && m.Port == currentPort { - m.Hostname = "" - m.Port = "" - m.Selector = template.URL(strings.TrimPrefix(string(m.Selector), "URL:")) - items[i].object = m - } - } -} - -func addOverrides(base *template.Template, overrides *template.Template) (*template.Template, error) { - if overrides == nil { - return base, nil - } - - tmpl := base - var err error - for _, override := range overrides.Templates() { - tmpl, err = tmpl.AddParseTree(override.Name(), override.Tree) - if err != nil { - return nil, err - } - } - - return tmpl, nil -} - var baseTmpl = template.Must(template.New("htmlconv").Parse(` {{ define "header" -}} diff --git a/gopher/gophermap/internal/templates.go b/gopher/gophermap/internal/templates.go new file mode 100644 index 0000000..cc8ba14 --- /dev/null +++ b/gopher/gophermap/internal/templates.go @@ -0,0 +1,153 @@ +package internal + +import ( + "bytes" + "text/template" + htemplate "html/template" + "strings" + + "tildegit.org/tjp/sliderule/gopher" + "tildegit.org/tjp/sliderule/internal/types" +) + +type renderItem struct { + Template string + Object any +} + +type renderRef struct { + Type types.Status + Display string + Selector htemplate.URL + Hostname string + Port string +} + +func simplifyLinks(items []renderItem, currentHost, currentPort string) { + for i, item := range items { + if item.Template != "link" && item.Template != "image" { + continue + } + + m := item.Object.(renderRef) + if m.Hostname == currentHost && m.Port == currentPort { + m.Hostname = "" + m.Port = "" + m.Selector = htemplate.URL(strings.TrimPrefix(string(m.Selector), "URL:")) + items[i].Object = m + } + } +} + +func AddOverrides(base *template.Template, overrides *template.Template) (*template.Template, error) { + if overrides == nil { + return base, nil + } + + tmpl := base + var err error + for _, override := range overrides.Templates() { + tmpl, err = tmpl.AddParseTree(override.Name(), override.Tree) + if err != nil { + return nil, err + } + } + + return tmpl, nil +} + +func AddHTMLOverrides(base *htemplate.Template, overrides *htemplate.Template) (*htemplate.Template, error) { + if overrides == nil { + return base, nil + } + + tmpl := base + var err error + for _, override := range overrides.Templates() { + tmpl, err = tmpl.AddParseTree(override.Name(), override.Tree) + if err != nil { + return nil, err + } + } + + return tmpl, nil +} + +func refItem(item gopher.MapItem) renderRef { + return renderRef{ + Type: item.Type, + Display: item.Display, + Selector: htemplate.URL(item.Selector), + Hostname: item.Hostname, + Port: item.Port, + } +} + +func RenderItems(doc gopher.MapDocument) []renderItem { + out := make([]renderItem, 0, len(doc)) + out = append(out, renderItem{ + Template: "header", + Object: doc, + }) + inMsg := false + msg := &bytes.Buffer{} + var currentHost, currentPort string + + for _, mapItem := range doc { + switch mapItem.Type { + case gopher.InfoMessageType: + if inMsg { + _, _ = msg.WriteString("\n") + } else { + msg.Reset() + } + _, _ = msg.WriteString(mapItem.Display) + inMsg = true + + if currentHost == "" { + currentHost = mapItem.Hostname + } + if currentPort == "" { + currentPort = mapItem.Port + } + case gopher.GifFileType, gopher.ImageFileType, gopher.BitmapType, gopher.PngImageFileType: + if inMsg { + out = append(out, renderItem{ + Template: "message", + Object: msg.String(), + }) + inMsg = false + } + out = append(out, renderItem{ + Template: "image", + Object: refItem(mapItem), + }) + default: + if inMsg { + out = append(out, renderItem{ + Template: "message", + Object: msg.String(), + }) + inMsg = false + } + out = append(out, renderItem{ + Template: "link", + Object: refItem(mapItem), + }) + } + } + + if inMsg { + out = append(out, renderItem{ + Template: "message", + Object: msg.String(), + }) + } + + simplifyLinks(out, currentHost, currentPort) + + return append(out, renderItem{ + Template: "footer", + Object: doc, + }) +} diff --git a/gopher/gophermap/mdconv/convert.go b/gopher/gophermap/mdconv/convert.go new file mode 100644 index 0000000..5d4da08 --- /dev/null +++ b/gopher/gophermap/mdconv/convert.go @@ -0,0 +1,52 @@ +package mdconv + +import ( + "io" + "text/template" + + "tildegit.org/tjp/sliderule/gopher" + "tildegit.org/tjp/sliderule/gopher/gophermap/internal" +) + +func Convert(wr io.Writer, doc gopher.MapDocument, overrides *template.Template) error { + tmpl, err := baseTmpl.Clone() + if err != nil { + return err + } + + tmpl, err = internal.AddOverrides(tmpl, overrides) + if err != nil { + return err + } + + for _, item := range internal.RenderItems(doc) { + if err := tmpl.ExecuteTemplate(wr, item.Template, item.Object); err != nil { + return err + } + } + + return nil +} + +var baseTmpl = template.Must(template.New("mdconv").Parse(` +{{ define "header" }}{{ end }} + +{{ define "message" -}} +` + "```" + ` +{{.}} +` + "```" + ` + +{{ end }} + +{{ define "link" -}} +[{{.Display}}]({{ if .Hostname | and .Port }}gopher://{{.Hostname}}:{{.Port}}{{ end }}{{.Selector}}) + +{{ end }} + +{{ define "image" -}} +![{{.Display}}]({{ if .Hostname | and .Port }}gopher://{{.Hostname}}:{{.Port}}{{ end }}{{.Selector}}) + +{{ end }} + +{{ define "footer" }}{{ end }} +`)) diff --git a/gopher/gophermap/mdconv/convert_test.go b/gopher/gophermap/mdconv/convert_test.go new file mode 100644 index 0000000..2e39106 --- /dev/null +++ b/gopher/gophermap/mdconv/convert_test.go @@ -0,0 +1,69 @@ +package mdconv + +import ( + "bytes" + "fmt" + "strings" + "testing" + + "tildegit.org/tjp/sliderule/gopher/gophermap" +) + +func TestConvert(t *testing.T) { + tests := []struct { + name string + input string + output string + }{ + { + name: "basic doc", + input: strings.ReplaceAll(` +iI am informational text localhost 70 +icontinued on this line localhost 70 +i localhost 70 +0this is my text file /file.txt localhost 70 +i localhost 70 +1here's a sub-menu /sub/ localhost 70 +. +`[1:], "\n", "\r\n"), + output: (` +` + "```" + ` +I am informational text +continued on this line + +` + "```" + ` + +[this is my text file](/file.txt) + +` + "```" + ` + +` + "```" + ` + +[here's a sub-menu](/sub/) + +`)[1:], + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + doc, err := gophermap.Parse(bytes.NewBufferString(test.input)) + if err != nil { + t.Fatal(err) + } + + buf := &bytes.Buffer{} + if err := Convert(buf, doc, nil); err != nil { + t.Fatal(err) + } + + if buf.String() != test.output { + fmt.Println(">>> expected:") + fmt.Println(test.output) + fmt.Println(">>> got:") + fmt.Println(buf.String()) + t.Fatal("html body mismatch") + } + }) + } +} -- cgit v1.2.3