...
1
2
3
4
5 package godoc
6
7 import (
8 "bytes"
9 "encoding/json"
10 "errors"
11 "log"
12 "os"
13 pathpkg "path"
14 "strings"
15 "time"
16
17 "golang.org/x/tools/godoc/vfs"
18 )
19
20 var (
21 doctype = []byte("<!DOCTYPE ")
22 jsonStart = []byte("<!--{")
23 jsonEnd = []byte("}-->")
24 )
25
26
27
28
29 type Metadata struct {
30
31 Title string
32 Subtitle string
33 Template bool
34 Path string
35 AltPaths []string
36
37
38 filePath string
39 }
40
41 func (m *Metadata) FilePath() string { return m.filePath }
42
43
44
45
46 func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
47 tail = b
48 if !bytes.HasPrefix(b, jsonStart) {
49 return
50 }
51 end := bytes.Index(b, jsonEnd)
52 if end < 0 {
53 return
54 }
55 b = b[len(jsonStart)-1 : end+1]
56 if err = json.Unmarshal(b, &meta); err != nil {
57 return
58 }
59 tail = tail[end+len(jsonEnd):]
60 return
61 }
62
63
64
65 func (c *Corpus) updateMetadata() {
66 metadata := make(map[string]*Metadata)
67 var scan func(string)
68 scan = func(dir string) {
69 fis, err := c.fs.ReadDir(dir)
70 if err != nil {
71 if dir == "/doc" && errors.Is(err, os.ErrNotExist) {
72
73 return
74 }
75 log.Printf("updateMetadata %s: %v", dir, err)
76 return
77 }
78 for _, fi := range fis {
79 name := pathpkg.Join(dir, fi.Name())
80 if fi.IsDir() {
81 scan(name)
82 continue
83 }
84 if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".md") {
85 continue
86 }
87
88 b, err := vfs.ReadFile(c.fs, name)
89 if err != nil {
90 log.Printf("updateMetadata %s: %v", name, err)
91 continue
92 }
93 meta, _, err := extractMetadata(b)
94 if err != nil {
95 log.Printf("updateMetadata: %s: %v", name, err)
96 continue
97 }
98
99
100 if strings.HasSuffix(name, ".md") {
101 name = strings.TrimSuffix(name, ".md") + ".html"
102 }
103
104 meta.filePath = name
105 if meta.Path == "" {
106
107 meta.Path = strings.TrimSuffix(name, ".html")
108 }
109
110 metadata[meta.Path] = &meta
111 metadata[meta.filePath] = &meta
112 for _, path := range meta.AltPaths {
113 metadata[path] = &meta
114 }
115 }
116 }
117 scan("/doc")
118 c.docMetadata.Set(metadata)
119 }
120
121
122
123 func (c *Corpus) MetadataFor(relpath string) *Metadata {
124 if m, _ := c.docMetadata.Get(); m != nil {
125 meta := m.(map[string]*Metadata)
126
127 if p := meta[relpath]; p != nil {
128 return p
129 }
130
131 if strings.HasSuffix(relpath, "/") {
132 relpath = relpath[:len(relpath)-1]
133 } else {
134 relpath = relpath + "/"
135 }
136 return meta[relpath]
137 }
138 return nil
139 }
140
141
142
143 func (c *Corpus) refreshMetadata() {
144 select {
145 case c.refreshMetadataSignal <- true:
146 default:
147 }
148 }
149
150
151
152 func (c *Corpus) refreshMetadataLoop() {
153 for {
154 <-c.refreshMetadataSignal
155 c.updateMetadata()
156 time.Sleep(10 * time.Second)
157 }
158 }
159
View as plain text