Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / meta.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/meta.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/meta.go
new file mode 100644 (file)
index 0000000..260833d
--- /dev/null
@@ -0,0 +1,150 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package godoc
+
+import (
+       "bytes"
+       "encoding/json"
+       "errors"
+       "log"
+       "os"
+       pathpkg "path"
+       "strings"
+       "time"
+
+       "golang.org/x/tools/godoc/vfs"
+)
+
+var (
+       doctype   = []byte("<!DOCTYPE ")
+       jsonStart = []byte("<!--{")
+       jsonEnd   = []byte("}-->")
+)
+
+// ----------------------------------------------------------------------------
+// Documentation Metadata
+
+// TODO(adg): why are some exported and some aren't? -brad
+type Metadata struct {
+       Title    string
+       Subtitle string
+       Template bool   // execute as template
+       Path     string // canonical path for this page
+       filePath string // filesystem path relative to goroot
+}
+
+func (m *Metadata) FilePath() string { return m.filePath }
+
+// extractMetadata extracts the Metadata from a byte slice.
+// It returns the Metadata value and the remaining data.
+// If no metadata is present the original byte slice is returned.
+//
+func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
+       tail = b
+       if !bytes.HasPrefix(b, jsonStart) {
+               return
+       }
+       end := bytes.Index(b, jsonEnd)
+       if end < 0 {
+               return
+       }
+       b = b[len(jsonStart)-1 : end+1] // drop leading <!-- and include trailing }
+       if err = json.Unmarshal(b, &meta); err != nil {
+               return
+       }
+       tail = tail[end+len(jsonEnd):]
+       return
+}
+
+// UpdateMetadata scans $GOROOT/doc for HTML files, reads their metadata,
+// and updates the DocMetadata map.
+func (c *Corpus) updateMetadata() {
+       metadata := make(map[string]*Metadata)
+       var scan func(string) // scan is recursive
+       scan = func(dir string) {
+               fis, err := c.fs.ReadDir(dir)
+               if err != nil {
+                       if dir == "/doc" && errors.Is(err, os.ErrNotExist) {
+                               // Be quiet during tests that don't have a /doc tree.
+                               return
+                       }
+                       log.Printf("updateMetadata %s: %v", dir, err)
+                       return
+               }
+               for _, fi := range fis {
+                       name := pathpkg.Join(dir, fi.Name())
+                       if fi.IsDir() {
+                               scan(name) // recurse
+                               continue
+                       }
+                       if !strings.HasSuffix(name, ".html") {
+                               continue
+                       }
+                       // Extract metadata from the file.
+                       b, err := vfs.ReadFile(c.fs, name)
+                       if err != nil {
+                               log.Printf("updateMetadata %s: %v", name, err)
+                               continue
+                       }
+                       meta, _, err := extractMetadata(b)
+                       if err != nil {
+                               log.Printf("updateMetadata: %s: %v", name, err)
+                               continue
+                       }
+                       // Store relative filesystem path in Metadata.
+                       meta.filePath = name
+                       if meta.Path == "" {
+                               // If no Path, canonical path is actual path.
+                               meta.Path = meta.filePath
+                       }
+                       // Store under both paths.
+                       metadata[meta.Path] = &meta
+                       metadata[meta.filePath] = &meta
+               }
+       }
+       scan("/doc")
+       c.docMetadata.Set(metadata)
+}
+
+// MetadataFor returns the *Metadata for a given relative path or nil if none
+// exists.
+//
+func (c *Corpus) MetadataFor(relpath string) *Metadata {
+       if m, _ := c.docMetadata.Get(); m != nil {
+               meta := m.(map[string]*Metadata)
+               // If metadata for this relpath exists, return it.
+               if p := meta[relpath]; p != nil {
+                       return p
+               }
+               // Try with or without trailing slash.
+               if strings.HasSuffix(relpath, "/") {
+                       relpath = relpath[:len(relpath)-1]
+               } else {
+                       relpath = relpath + "/"
+               }
+               return meta[relpath]
+       }
+       return nil
+}
+
+// refreshMetadata sends a signal to update DocMetadata. If a refresh is in
+// progress the metadata will be refreshed again afterward.
+//
+func (c *Corpus) refreshMetadata() {
+       select {
+       case c.refreshMetadataSignal <- true:
+       default:
+       }
+}
+
+// RefreshMetadataLoop runs forever, updating DocMetadata when the underlying
+// file system changes. It should be launched in a goroutine.
+func (c *Corpus) refreshMetadataLoop() {
+       for {
+               <-c.refreshMetadataSignal
+               c.updateMetadata()
+               time.Sleep(10 * time.Second) // at most once every 10 seconds
+       }
+}