mirror of
https://github.com/golang/go
synced 2024-11-20 03:34:40 -07:00
cmd/godoc: emit id's for constants and variables
Fixes #5077. R=r CC=golang-dev https://golang.org/cl/8021044
This commit is contained in:
parent
8d4f381f5c
commit
611e8dbf52
@ -25,36 +25,41 @@ import (
|
|||||||
// formatted the same way as with FormatText.
|
// formatted the same way as with FormatText.
|
||||||
//
|
//
|
||||||
func LinkifyText(w io.Writer, text []byte, n ast.Node) {
|
func LinkifyText(w io.Writer, text []byte, n ast.Node) {
|
||||||
links := links(n)
|
links := linksFor(n)
|
||||||
|
|
||||||
i := 0 // links index
|
i := 0 // links index
|
||||||
open := false // status of html tag
|
prev := "" // prev HTML tag
|
||||||
linkWriter := func(w io.Writer, _ int, start bool) {
|
linkWriter := func(w io.Writer, _ int, start bool) {
|
||||||
// end tag
|
// end tag
|
||||||
if !start {
|
if !start {
|
||||||
if open {
|
if prev != "" {
|
||||||
fmt.Fprintf(w, `</a>`)
|
fmt.Fprintf(w, `</%s>`, prev)
|
||||||
open = false
|
prev = ""
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// start tag
|
// start tag
|
||||||
open = false
|
prev = ""
|
||||||
if i < len(links) {
|
if i < len(links) {
|
||||||
switch info := links[i]; {
|
switch info := links[i]; {
|
||||||
case info.path != "" && info.ident == nil:
|
case info.path != "" && info.name == "":
|
||||||
// package path
|
// package path
|
||||||
fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
|
fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
|
||||||
open = true
|
prev = "a"
|
||||||
case info.path != "" && info.ident != nil:
|
case info.path != "" && info.name != "":
|
||||||
// qualified identifier
|
// qualified identifier
|
||||||
fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.ident.Name)
|
fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name)
|
||||||
open = true
|
prev = "a"
|
||||||
case info.path == "" && info.ident != nil:
|
case info.path == "" && info.name != "":
|
||||||
// locally declared identifier
|
// local identifier
|
||||||
fmt.Fprintf(w, `<a href="#%s">`, info.ident.Name)
|
if info.mode == identVal {
|
||||||
open = true
|
fmt.Fprintf(w, `<span id="%s">`, info.name)
|
||||||
|
prev = "span"
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `<a href="#%s">`, info.name)
|
||||||
|
prev = "a"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -69,27 +74,34 @@ func LinkifyText(w io.Writer, text []byte, n ast.Node) {
|
|||||||
// The zero value of a link represents "no link".
|
// The zero value of a link represents "no link".
|
||||||
//
|
//
|
||||||
type link struct {
|
type link struct {
|
||||||
path string
|
mode identMode
|
||||||
ident *ast.Ident
|
path, name string // package path, identifier name
|
||||||
}
|
}
|
||||||
|
|
||||||
// links returns the list of links for the identifiers used
|
// linksFor returns the list of links for the identifiers used
|
||||||
// by node in the same order as they appear in the source.
|
// by node in the same order as they appear in the source.
|
||||||
//
|
//
|
||||||
func links(node ast.Node) (list []link) {
|
func linksFor(node ast.Node) (list []link) {
|
||||||
defs := defs(node)
|
modes := identModesFor(node)
|
||||||
|
|
||||||
// NOTE: We are expecting ast.Inspect to call the
|
// NOTE: We are expecting ast.Inspect to call the
|
||||||
// callback function in source text order.
|
// callback function in source text order.
|
||||||
ast.Inspect(node, func(node ast.Node) bool {
|
ast.Inspect(node, func(node ast.Node) bool {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
info := link{}
|
m := modes[n]
|
||||||
if !defs[n] {
|
info := link{mode: m}
|
||||||
|
switch m {
|
||||||
|
case identUse:
|
||||||
if n.Obj == nil && predeclared[n.Name] {
|
if n.Obj == nil && predeclared[n.Name] {
|
||||||
info.path = builtinPkgPath
|
info.path = builtinPkgPath
|
||||||
}
|
}
|
||||||
info.ident = n
|
info.name = n.Name
|
||||||
|
case identDef:
|
||||||
|
// any declaration expect const or var - empty link
|
||||||
|
case identVal:
|
||||||
|
// const or var declaration
|
||||||
|
info.name = n.Name
|
||||||
}
|
}
|
||||||
list = append(list, info)
|
list = append(list, info)
|
||||||
return false
|
return false
|
||||||
@ -107,7 +119,7 @@ func links(node ast.Node) (list []link) {
|
|||||||
// and one for the qualified identifier.
|
// and one for the qualified identifier.
|
||||||
info := link{path: path}
|
info := link{path: path}
|
||||||
list = append(list, info)
|
list = append(list, info)
|
||||||
info.ident = n.Sel
|
info.name = n.Sel.Name
|
||||||
list = append(list, info)
|
list = append(list, info)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -121,28 +133,37 @@ func links(node ast.Node) (list []link) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// defs returns the set of identifiers that are declared ("defined") by node.
|
// The identMode describes how an identifier is "used" at its source location.
|
||||||
func defs(node ast.Node) map[*ast.Ident]bool {
|
type identMode int
|
||||||
m := make(map[*ast.Ident]bool)
|
|
||||||
|
const (
|
||||||
|
identUse identMode = iota // identifier is used (must be zero value for identMode)
|
||||||
|
identDef // identifier is defined
|
||||||
|
identVal // identifier is defined in a const or var declaration
|
||||||
|
)
|
||||||
|
|
||||||
|
// identModesFor returns a map providing the identMode for each identifier used by node.
|
||||||
|
func identModesFor(node ast.Node) map[*ast.Ident]identMode {
|
||||||
|
m := make(map[*ast.Ident]identMode)
|
||||||
|
|
||||||
ast.Inspect(node, func(node ast.Node) bool {
|
ast.Inspect(node, func(node ast.Node) bool {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *ast.Field:
|
case *ast.Field:
|
||||||
for _, n := range n.Names {
|
for _, n := range n.Names {
|
||||||
m[n] = true
|
m[n] = identDef
|
||||||
}
|
}
|
||||||
case *ast.ImportSpec:
|
case *ast.ImportSpec:
|
||||||
if name := n.Name; name != nil {
|
if name := n.Name; name != nil {
|
||||||
m[name] = true
|
m[name] = identDef
|
||||||
}
|
}
|
||||||
case *ast.ValueSpec:
|
case *ast.ValueSpec:
|
||||||
for _, n := range n.Names {
|
for _, n := range n.Names {
|
||||||
m[n] = true
|
m[n] = identVal
|
||||||
}
|
}
|
||||||
case *ast.TypeSpec:
|
case *ast.TypeSpec:
|
||||||
m[n.Name] = true
|
m[n.Name] = identDef
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
m[n.Name] = true
|
m[n.Name] = identDef
|
||||||
case *ast.AssignStmt:
|
case *ast.AssignStmt:
|
||||||
// Short variable declarations only show up if we apply
|
// Short variable declarations only show up if we apply
|
||||||
// this code to all source code (as opposed to exported
|
// this code to all source code (as opposed to exported
|
||||||
@ -155,7 +176,7 @@ func defs(node ast.Node) map[*ast.Ident]bool {
|
|||||||
// Each lhs expression should be an
|
// Each lhs expression should be an
|
||||||
// ident, but we are conservative and check.
|
// ident, but we are conservative and check.
|
||||||
if n, _ := x.(*ast.Ident); n != nil {
|
if n, _ := x.(*ast.Ident); n != nil {
|
||||||
m[n] = true
|
m[n] = identVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +188,9 @@ func defs(node ast.Node) map[*ast.Ident]bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The predeclared map represents the set of all predeclared identifiers.
|
// The predeclared map represents the set of all predeclared identifiers.
|
||||||
|
// TODO(gri) This information is also encoded in similar maps in go/doc,
|
||||||
|
// but not exported. Consider exporting an accessor and using
|
||||||
|
// it instead.
|
||||||
var predeclared = map[string]bool{
|
var predeclared = map[string]bool{
|
||||||
"bool": true,
|
"bool": true,
|
||||||
"byte": true,
|
"byte": true,
|
||||||
|
@ -411,7 +411,7 @@ func main() {
|
|||||||
info.PDoc.ImportPath = flag.Arg(0)
|
info.PDoc.ImportPath = flag.Arg(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have more than one argument, use the remaining arguments for filtering
|
// If we have more than one argument, use the remaining arguments for filtering.
|
||||||
if flag.NArg() > 1 {
|
if flag.NArg() > 1 {
|
||||||
args := flag.Args()[1:]
|
args := flag.Args()[1:]
|
||||||
rx := makeRx(args)
|
rx := makeRx(args)
|
||||||
|
Loading…
Reference in New Issue
Block a user