diff --git a/src/cmd/godoc/linkify.go b/src/cmd/godoc/linkify.go index 1f976951b5..5b4862419e 100644 --- a/src/cmd/godoc/linkify.go +++ b/src/cmd/godoc/linkify.go @@ -25,36 +25,41 @@ import ( // formatted the same way as with FormatText. // func LinkifyText(w io.Writer, text []byte, n ast.Node) { - links := links(n) + links := linksFor(n) - i := 0 // links index - open := false // status of html tag + i := 0 // links index + prev := "" // prev HTML tag linkWriter := func(w io.Writer, _ int, start bool) { // end tag if !start { - if open { - fmt.Fprintf(w, ``) - open = false + if prev != "" { + fmt.Fprintf(w, ``, prev) + prev = "" } return } // start tag - open = false + prev = "" if i < len(links) { switch info := links[i]; { - case info.path != "" && info.ident == nil: + case info.path != "" && info.name == "": // package path fmt.Fprintf(w, ``, info.path) - open = true - case info.path != "" && info.ident != nil: + prev = "a" + case info.path != "" && info.name != "": // qualified identifier - fmt.Fprintf(w, ``, info.path, info.ident.Name) - open = true - case info.path == "" && info.ident != nil: - // locally declared identifier - fmt.Fprintf(w, ``, info.ident.Name) - open = true + fmt.Fprintf(w, ``, info.path, info.name) + prev = "a" + case info.path == "" && info.name != "": + // local identifier + if info.mode == identVal { + fmt.Fprintf(w, ``, info.name) + prev = "span" + } else { + fmt.Fprintf(w, ``, info.name) + prev = "a" + } } i++ } @@ -69,27 +74,34 @@ func LinkifyText(w io.Writer, text []byte, n ast.Node) { // The zero value of a link represents "no link". // type link struct { - path string - ident *ast.Ident + mode identMode + 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. // -func links(node ast.Node) (list []link) { - defs := defs(node) +func linksFor(node ast.Node) (list []link) { + modes := identModesFor(node) // NOTE: We are expecting ast.Inspect to call the // callback function in source text order. ast.Inspect(node, func(node ast.Node) bool { switch n := node.(type) { case *ast.Ident: - info := link{} - if !defs[n] { + m := modes[n] + info := link{mode: m} + switch m { + case identUse: if n.Obj == nil && predeclared[n.Name] { 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) return false @@ -107,7 +119,7 @@ func links(node ast.Node) (list []link) { // and one for the qualified identifier. info := link{path: path} list = append(list, info) - info.ident = n.Sel + info.name = n.Sel.Name list = append(list, info) return false } @@ -121,28 +133,37 @@ func links(node ast.Node) (list []link) { return } -// defs returns the set of identifiers that are declared ("defined") by node. -func defs(node ast.Node) map[*ast.Ident]bool { - m := make(map[*ast.Ident]bool) +// The identMode describes how an identifier is "used" at its source location. +type identMode int + +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 { switch n := node.(type) { case *ast.Field: for _, n := range n.Names { - m[n] = true + m[n] = identDef } case *ast.ImportSpec: if name := n.Name; name != nil { - m[name] = true + m[name] = identDef } case *ast.ValueSpec: for _, n := range n.Names { - m[n] = true + m[n] = identVal } case *ast.TypeSpec: - m[n.Name] = true + m[n.Name] = identDef case *ast.FuncDecl: - m[n.Name] = true + m[n.Name] = identDef case *ast.AssignStmt: // Short variable declarations only show up if we apply // 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 // ident, but we are conservative and check. 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. +// 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{ "bool": true, "byte": true, diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index d61141530e..ab792c8af0 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -411,7 +411,7 @@ func main() { 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 { args := flag.Args()[1:] rx := makeRx(args)