GoPLS Viewer

Home|gopls/godoc/meta.go
1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package godoc
6
7import (
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
20var (
21    doctype   = []byte("<!DOCTYPE ")
22    jsonStart = []byte("<!--{")
23    jsonEnd   = []byte("}-->")
24)
25
26// ----------------------------------------------------------------------------
27// Documentation Metadata
28
29type Metadata struct {
30    // These fields can be set in the JSON header at the top of a doc.
31    Title    string
32    Subtitle string
33    Template bool     // execute as template
34    Path     string   // canonical path for this page
35    AltPaths []string // redirect these other paths to this page
36
37    // These are internal to the implementation.
38    filePath string // filesystem path relative to goroot
39}
40
41func (m *MetadataFilePath() string { return m.filePath }
42
43// extractMetadata extracts the Metadata from a byte slice.
44// It returns the Metadata value and the remaining data.
45// If no metadata is present the original byte slice is returned.
46func extractMetadata(b []byte) (meta Metadatatail []byteerr error) {
47    tail = b
48    if !bytes.HasPrefix(bjsonStart) {
49        return
50    }
51    end := bytes.Index(bjsonEnd)
52    if end < 0 {
53        return
54    }
55    b = b[len(jsonStart)-1 : end+1// drop leading <!-- and include trailing }
56    if err = json.Unmarshal(b, &meta); err != nil {
57        return
58    }
59    tail = tail[end+len(jsonEnd):]
60    return
61}
62
63// UpdateMetadata scans $GOROOT/doc for HTML and Markdown files, reads their metadata,
64// and updates the DocMetadata map.
65func (c *CorpusupdateMetadata() {
66    metadata := make(map[string]*Metadata)
67    var scan func(string// scan is recursive
68    scan = func(dir string) {
69        fiserr := c.fs.ReadDir(dir)
70        if err != nil {
71            if dir == "/doc" && errors.Is(erros.ErrNotExist) {
72                // Be quiet during tests that don't have a /doc tree.
73                return
74            }
75            log.Printf("updateMetadata %s: %v"direrr)
76            return
77        }
78        for _fi := range fis {
79            name := pathpkg.Join(dirfi.Name())
80            if fi.IsDir() {
81                scan(name// recurse
82                continue
83            }
84            if !strings.HasSuffix(name".html") && !strings.HasSuffix(name".md") {
85                continue
86            }
87            // Extract metadata from the file.
88            berr := vfs.ReadFile(c.fsname)
89            if err != nil {
90                log.Printf("updateMetadata %s: %v"nameerr)
91                continue
92            }
93            meta_err := extractMetadata(b)
94            if err != nil {
95                log.Printf("updateMetadata: %s: %v"nameerr)
96                continue
97            }
98            // Present all .md as if they were .html,
99            // so that it doesn't matter which one a page is written in.
100            if strings.HasSuffix(name".md") {
101                name = strings.TrimSuffix(name".md") + ".html"
102            }
103            // Store relative filesystem path in Metadata.
104            meta.filePath = name
105            if meta.Path == "" {
106                // If no Path, canonical path is actual path with .html removed.
107                meta.Path = strings.TrimSuffix(name".html")
108            }
109            // Store under both paths.
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// MetadataFor returns the *Metadata for a given relative path or nil if none
122// exists.
123func (c *CorpusMetadataFor(relpath string) *Metadata {
124    if m_ := c.docMetadata.Get(); m != nil {
125        meta := m.(map[string]*Metadata)
126        // If metadata for this relpath exists, return it.
127        if p := meta[relpath]; p != nil {
128            return p
129        }
130        // Try with or without trailing slash.
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// refreshMetadata sends a signal to update DocMetadata. If a refresh is in
142// progress the metadata will be refreshed again afterward.
143func (c *CorpusrefreshMetadata() {
144    select {
145    case c.refreshMetadataSignal <- true:
146    default:
147    }
148}
149
150// RefreshMetadataLoop runs forever, updating DocMetadata when the underlying
151// file system changes. It should be launched in a goroutine.
152func (c *CorpusrefreshMetadataLoop() {
153    for {
154        <-c.refreshMetadataSignal
155        c.updateMetadata()
156        time.Sleep(10 * time.Second// at most once every 10 seconds
157    }
158}
159
MembersX
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt.b
extractMetadata.tail
Corpus.MetadataFor._
Corpus.refreshMetadata
Metadata.Template
extractMetadata
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.fi
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt._
Corpus.MetadataFor.relpath
Corpus.refreshMetadata.c
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt.err
Corpus.MetadataFor.m
json
Metadata.Subtitle
Metadata.filePath
extractMetadata.b
extractMetadata.end
Corpus.updateMetadata.metadata
Corpus.refreshMetadataLoop
Metadata.Path
Metadata.AltPaths
Metadata.FilePath.m
Corpus.updateMetadata.BlockStmt.err
Metadata.FilePath
extractMetadata.meta
extractMetadata.err
Corpus.updateMetadata.BlockStmt.fis
Corpus.MetadataFor.c
Corpus.refreshMetadataLoop.c
Corpus.updateMetadata
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt.name
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt.meta
Metadata
Metadata.Title
Corpus.updateMetadata.c
Corpus.updateMetadata.BlockStmt.RangeStmt_2026.BlockStmt.RangeStmt_3067.path
Corpus.MetadataFor
Members
X