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
|
//go:build ignore
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"dmitri.shuralyov.com/text/kebabcase"
"github.com/shurcooL/go-goon"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
var oFlag = flag.String("o", "", "write output to `file` (default standard output)")
func main() {
flag.Parse()
err := run()
if err != nil {
log.Fatalln(err)
}
}
func run() error {
f, err := os.Open(filepath.Join("_data", "data.json"))
if err != nil {
return err
}
defer f.Close()
var octicons map[string]octicon
err = json.NewDecoder(f).Decode(&octicons)
if err != nil {
return err
}
var names []string
for name := range octicons {
names = append(names, name)
}
sort.Strings(names)
var buf bytes.Buffer
fmt.Fprint(&buf, `package octicon
import (
"strconv"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
// Icon returns the named Octicon SVG node.
// It returns nil if name is not a valid Octicon symbol name.
func Icon(name string) *html.Node {
switch name {
`)
for _, name := range names {
fmt.Fprintf(&buf, " case %q:\n return %v()\n", name, kebabcase.Parse(name).ToMixedCaps())
}
fmt.Fprint(&buf, ` default:
return nil
}
}
// SetSize sets size of icon, and returns a reference to it.
func SetSize(icon *html.Node, size int) *html.Node {
icon.Attr[`, widthAttrIndex, `].Val = strconv.Itoa(size)
icon.Attr[`, heightAttrIndex, `].Val = strconv.Itoa(size)
return icon
}
`)
// Write all individual Octicon functions.
for _, name := range names {
generateAndWriteOcticon(&buf, octicons, name)
}
var w io.Writer
switch *oFlag {
case "":
w = os.Stdout
default:
f, err := os.Create(*oFlag)
if err != nil {
return err
}
defer f.Close()
w = f
}
_, err = w.Write(buf.Bytes())
return err
}
type octicon struct {
Path string
Width int
Height float64
}
func generateAndWriteOcticon(w io.Writer, octicons map[string]octicon, name string) {
svgXML := generateOcticon(octicons[name])
svg := parseOcticon(svgXML)
// Clear these fields to remove cycles in the data structure, since go-goon
// cannot print those in a way that's valid Go code. The generated data structure
// is not a proper *html.Node with all fields set, but it's enough for rendering
// to be successful.
svg.LastChild = nil
svg.FirstChild.Parent = nil
fmt.Fprintln(w)
fmt.Fprintf(w, "// %s returns an %q Octicon SVG node.\n", kebabcase.Parse(name).ToMixedCaps(), name)
fmt.Fprintf(w, "func %s() *html.Node {\n", kebabcase.Parse(name).ToMixedCaps())
fmt.Fprint(w, " return ")
goon.Fdump(w, svg)
fmt.Fprintln(w, "}")
}
// These constants are used during generation of SetSize function.
// Keep them in sync with generateOcticon below.
const (
widthAttrIndex = 1
heightAttrIndex = 2
)
// TODO: Short-circuit generateOcticon and parseOcticon.
func generateOcticon(o octicon) (svgXML string) {
path := o.Path
if strings.HasPrefix(path, `<path fill-rule="evenodd" `) {
// Skip fill-rule, if present. It has no effect on displayed SVG, but takes up space.
path = `<path ` + path[len(`<path fill-rule="evenodd" `):]
}
// Note, SetSize relies on the absolute position of the width, height attributes.
// Keep them in sync with widthAttrIndex and heightAttrIndex.
return fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width=16 height=16 viewBox="0 0 %v %v">%s</svg>`,
o.Width, o.Height, path)
}
func parseOcticon(svgXML string) *html.Node {
e, err := html.ParseFragment(strings.NewReader(svgXML), nil)
if err != nil {
panic(fmt.Errorf("internal error: html.ParseFragment failed: %v", err))
}
svg := e[0].LastChild.FirstChild // TODO: Is there a better way to just get the <svg>...</svg> element directly, skipping <html><head></head><body><svg>...</svg></body></html>?
svg.Parent.RemoveChild(svg)
svg.Attr = append(svg.Attr, html.Attribute{Key: atom.Style.String(), Val: `fill: currentColor; vertical-align: top;`})
return svg
}
|