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 / linkify.go
1 // Copyright 2013 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
5 // This file implements LinkifyText which introduces
6 // links for identifiers pointing to their declarations.
7 // The approach does not cover all cases because godoc
8 // doesn't have complete type information, but it's
9 // reasonably good for browsing.
10
11 package godoc
12
13 import (
14         "fmt"
15         "go/ast"
16         "go/doc"
17         "go/token"
18         "io"
19         "strconv"
20 )
21
22 // LinkifyText HTML-escapes source text and writes it to w.
23 // Identifiers that are in a "use" position (i.e., that are
24 // not being declared), are wrapped with HTML links pointing
25 // to the respective declaration, if possible. Comments are
26 // formatted the same way as with FormatText.
27 //
28 func LinkifyText(w io.Writer, text []byte, n ast.Node) {
29         links := linksFor(n)
30
31         i := 0     // links index
32         prev := "" // prev HTML tag
33         linkWriter := func(w io.Writer, _ int, start bool) {
34                 // end tag
35                 if !start {
36                         if prev != "" {
37                                 fmt.Fprintf(w, `</%s>`, prev)
38                                 prev = ""
39                         }
40                         return
41                 }
42
43                 // start tag
44                 prev = ""
45                 if i < len(links) {
46                         switch info := links[i]; {
47                         case info.path != "" && info.name == "":
48                                 // package path
49                                 fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
50                                 prev = "a"
51                         case info.path != "" && info.name != "":
52                                 // qualified identifier
53                                 fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name)
54                                 prev = "a"
55                         case info.path == "" && info.name != "":
56                                 // local identifier
57                                 if info.isVal {
58                                         fmt.Fprintf(w, `<span id="%s">`, info.name)
59                                         prev = "span"
60                                 } else if ast.IsExported(info.name) {
61                                         fmt.Fprintf(w, `<a href="#%s">`, info.name)
62                                         prev = "a"
63                                 }
64                         }
65                         i++
66                 }
67         }
68
69         idents := tokenSelection(text, token.IDENT)
70         comments := tokenSelection(text, token.COMMENT)
71         FormatSelections(w, text, linkWriter, idents, selectionTag, comments)
72 }
73
74 // A link describes the (HTML) link information for an identifier.
75 // The zero value of a link represents "no link".
76 //
77 type link struct {
78         path, name string // package path, identifier name
79         isVal      bool   // identifier is defined in a const or var declaration
80 }
81
82 // linksFor returns the list of links for the identifiers used
83 // by node in the same order as they appear in the source.
84 //
85 func linksFor(node ast.Node) (links []link) {
86         // linkMap tracks link information for each ast.Ident node. Entries may
87         // be created out of source order (for example, when we visit a parent
88         // definition node). These links are appended to the returned slice when
89         // their ast.Ident nodes are visited.
90         linkMap := make(map[*ast.Ident]link)
91
92         ast.Inspect(node, func(node ast.Node) bool {
93                 switch n := node.(type) {
94                 case *ast.Field:
95                         for _, n := range n.Names {
96                                 linkMap[n] = link{}
97                         }
98                 case *ast.ImportSpec:
99                         if name := n.Name; name != nil {
100                                 linkMap[name] = link{}
101                         }
102                 case *ast.ValueSpec:
103                         for _, n := range n.Names {
104                                 linkMap[n] = link{name: n.Name, isVal: true}
105                         }
106                 case *ast.FuncDecl:
107                         linkMap[n.Name] = link{}
108                 case *ast.TypeSpec:
109                         linkMap[n.Name] = link{}
110                 case *ast.AssignStmt:
111                         // Short variable declarations only show up if we apply
112                         // this code to all source code (as opposed to exported
113                         // declarations only).
114                         if n.Tok == token.DEFINE {
115                                 // Some of the lhs variables may be re-declared,
116                                 // so technically they are not defs. We don't
117                                 // care for now.
118                                 for _, x := range n.Lhs {
119                                         // Each lhs expression should be an
120                                         // ident, but we are conservative and check.
121                                         if n, _ := x.(*ast.Ident); n != nil {
122                                                 linkMap[n] = link{isVal: true}
123                                         }
124                                 }
125                         }
126                 case *ast.SelectorExpr:
127                         // Detect qualified identifiers of the form pkg.ident.
128                         // If anything fails we return true and collect individual
129                         // identifiers instead.
130                         if x, _ := n.X.(*ast.Ident); x != nil {
131                                 // Create links only if x is a qualified identifier.
132                                 if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
133                                         if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
134                                                 // spec.Path.Value is the import path
135                                                 if path, err := strconv.Unquote(spec.Path.Value); err == nil {
136                                                         // Register two links, one for the package
137                                                         // and one for the qualified identifier.
138                                                         linkMap[x] = link{path: path}
139                                                         linkMap[n.Sel] = link{path: path, name: n.Sel.Name}
140                                                 }
141                                         }
142                                 }
143                         }
144                 case *ast.CompositeLit:
145                         // Detect field names within composite literals. These links should
146                         // be prefixed by the type name.
147                         fieldPath := ""
148                         prefix := ""
149                         switch typ := n.Type.(type) {
150                         case *ast.Ident:
151                                 prefix = typ.Name + "."
152                         case *ast.SelectorExpr:
153                                 if x, _ := typ.X.(*ast.Ident); x != nil {
154                                         // Create links only if x is a qualified identifier.
155                                         if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
156                                                 if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
157                                                         // spec.Path.Value is the import path
158                                                         if path, err := strconv.Unquote(spec.Path.Value); err == nil {
159                                                                 // Register two links, one for the package
160                                                                 // and one for the qualified identifier.
161                                                                 linkMap[x] = link{path: path}
162                                                                 linkMap[typ.Sel] = link{path: path, name: typ.Sel.Name}
163                                                                 fieldPath = path
164                                                                 prefix = typ.Sel.Name + "."
165                                                         }
166                                                 }
167                                         }
168                                 }
169                         }
170                         for _, e := range n.Elts {
171                                 if kv, ok := e.(*ast.KeyValueExpr); ok {
172                                         if k, ok := kv.Key.(*ast.Ident); ok {
173                                                 // Note: there is some syntactic ambiguity here. We cannot determine
174                                                 // if this is a struct literal or a map literal without type
175                                                 // information. We assume struct literal.
176                                                 name := prefix + k.Name
177                                                 linkMap[k] = link{path: fieldPath, name: name}
178                                         }
179                                 }
180                         }
181                 case *ast.Ident:
182                         if l, ok := linkMap[n]; ok {
183                                 links = append(links, l)
184                         } else {
185                                 l := link{name: n.Name}
186                                 if n.Obj == nil && doc.IsPredeclared(n.Name) {
187                                         l.path = builtinPkgPath
188                                 }
189                                 links = append(links, l)
190                         }
191                 }
192                 return true
193         })
194         return
195 }