mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
This change removes the explicit configuration for improved documentation on hover. We use a comment's synopsis rather than the full comment. However, we also add a "noDocsOnHover" setting that is used by the cmd tests. Ultimately, no one should use this setting and we should remove it. We leave it temporarily because the cmd tests still need work. Change-Id: I5488eca96a729ed7edad8f59b95af163903740d6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/174378 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
// Copyright 2019 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 source
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/doc"
|
|
"go/format"
|
|
"go/token"
|
|
"go/types"
|
|
"strings"
|
|
)
|
|
|
|
// formatter returns the a hover value formatted with its documentation.
|
|
type formatter func(interface{}, *ast.CommentGroup) (string, error)
|
|
|
|
func (i *IdentifierInfo) Hover(ctx context.Context, qf types.Qualifier, markdownSupported, wantComments bool) (string, error) {
|
|
file := i.File.GetAST(ctx)
|
|
if qf == nil {
|
|
pkg := i.File.GetPackage(ctx)
|
|
qf = qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
|
|
}
|
|
var b strings.Builder
|
|
f := func(x interface{}, c *ast.CommentGroup) (string, error) {
|
|
if !wantComments {
|
|
c = nil
|
|
}
|
|
return writeHover(x, i.File.GetFileSet(ctx), &b, c, markdownSupported, qf)
|
|
}
|
|
obj := i.Declaration.Object
|
|
switch node := i.Declaration.Node.(type) {
|
|
case *ast.GenDecl:
|
|
switch obj := obj.(type) {
|
|
case *types.TypeName, *types.Var, *types.Const, *types.Func:
|
|
return formatGenDecl(node, obj, obj.Type(), f)
|
|
}
|
|
case *ast.FuncDecl:
|
|
if _, ok := obj.(*types.Func); ok {
|
|
return f(obj, node.Doc)
|
|
}
|
|
}
|
|
return f(obj, nil)
|
|
}
|
|
|
|
func formatGenDecl(node *ast.GenDecl, obj types.Object, typ types.Type, f formatter) (string, error) {
|
|
if _, ok := typ.(*types.Named); ok {
|
|
switch typ.Underlying().(type) {
|
|
case *types.Interface, *types.Struct:
|
|
return formatGenDecl(node, obj, typ.Underlying(), f)
|
|
}
|
|
}
|
|
var spec ast.Spec
|
|
for _, s := range node.Specs {
|
|
if s.Pos() <= obj.Pos() && obj.Pos() <= s.End() {
|
|
spec = s
|
|
break
|
|
}
|
|
}
|
|
if spec == nil {
|
|
return "", fmt.Errorf("no spec for node %v at position %v", node, obj.Pos())
|
|
}
|
|
// If we have a field or method.
|
|
switch obj.(type) {
|
|
case *types.Var, *types.Const, *types.Func:
|
|
return formatVar(spec, obj, f)
|
|
}
|
|
// Handle types.
|
|
switch spec := spec.(type) {
|
|
case *ast.TypeSpec:
|
|
if len(node.Specs) > 1 {
|
|
// If multiple types are declared in the same block.
|
|
return f(spec.Type, spec.Doc)
|
|
} else {
|
|
return f(spec, node.Doc)
|
|
}
|
|
case *ast.ValueSpec:
|
|
return f(spec, spec.Doc)
|
|
case *ast.ImportSpec:
|
|
return f(spec, spec.Doc)
|
|
}
|
|
return "", fmt.Errorf("unable to format spec %v (%T)", spec, spec)
|
|
}
|
|
|
|
func formatVar(node ast.Spec, obj types.Object, f formatter) (string, error) {
|
|
var fieldList *ast.FieldList
|
|
if spec, ok := node.(*ast.TypeSpec); ok {
|
|
switch t := spec.Type.(type) {
|
|
case *ast.StructType:
|
|
fieldList = t.Fields
|
|
case *ast.InterfaceType:
|
|
fieldList = t.Methods
|
|
}
|
|
}
|
|
// If we have a struct or interface declaration,
|
|
// we need to match the object to the corresponding field or method.
|
|
if fieldList != nil {
|
|
for i := 0; i < len(fieldList.List); i++ {
|
|
field := fieldList.List[i]
|
|
if field.Pos() <= obj.Pos() && obj.Pos() <= field.End() {
|
|
if field.Doc.Text() != "" {
|
|
return f(obj, field.Doc)
|
|
} else if field.Comment.Text() != "" {
|
|
return f(obj, field.Comment)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// If we weren't able to find documentation for the object.
|
|
return f(obj, nil)
|
|
}
|
|
|
|
// writeHover writes the hover for a given node and its documentation.
|
|
func writeHover(x interface{}, fset *token.FileSet, b *strings.Builder, c *ast.CommentGroup, markdownSupported bool, qf types.Qualifier) (string, error) {
|
|
if c != nil {
|
|
// TODO(rstambler): Improve conversion from Go docs to markdown.
|
|
b.WriteString(doc.Synopsis(c.Text()))
|
|
b.WriteRune('\n')
|
|
}
|
|
if markdownSupported {
|
|
b.WriteString("```go\n")
|
|
}
|
|
switch x := x.(type) {
|
|
case ast.Node:
|
|
if err := format.Node(b, fset, x); err != nil {
|
|
return "", err
|
|
}
|
|
case types.Object:
|
|
b.WriteString(types.ObjectString(x, qf))
|
|
}
|
|
if markdownSupported {
|
|
b.WriteString("\n```")
|
|
}
|
|
return b.String(), nil
|
|
}
|