| // Copyright 201p 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 ( |
| "bytes" |
| "context" |
| "fmt" |
| "go/ast" |
| "go/format" |
| "go/token" |
| "go/types" |
| ) |
| |
| func (i *IdentifierInfo) Hover(ctx context.Context, q types.Qualifier, enhancedHover bool) (string, string, error) { |
| file := i.File.GetAST(ctx) |
| if q == nil { |
| pkg := i.File.GetPackage(ctx) |
| q = qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()) |
| } |
| // TODO(rstambler): Remove this configuration when hover behavior is stable. |
| if enhancedHover { |
| switch obj := i.Declaration.Object.(type) { |
| case *types.TypeName: |
| if node, ok := i.Declaration.Node.(*ast.GenDecl); ok { |
| if decl, doc, err := formatTypeName(i.File.GetFileSet(ctx), node, obj, q); err == nil { |
| return decl, doc, nil |
| } else { |
| // Swallow errors so we can return a best-effort response using types.TypeString. |
| i.File.View().Logger().Errorf(ctx, "no hover for TypeName %v: %v", obj.Name(), err) |
| } |
| } |
| return types.TypeString(obj.Type(), q), "", nil |
| default: |
| return types.ObjectString(obj, q), "", nil |
| } |
| } |
| return types.ObjectString(i.Declaration.Object, q), "", nil |
| } |
| |
| func formatTypeName(fset *token.FileSet, decl *ast.GenDecl, obj *types.TypeName, q types.Qualifier) (string, string, error) { |
| if types.IsInterface(obj.Type()) { |
| return "", "", fmt.Errorf("no support for interfaces yet") |
| } |
| switch t := obj.Type().(type) { |
| case *types.Struct: |
| return formatStructType(fset, decl, t) |
| case *types.Named: |
| if under, ok := t.Underlying().(*types.Struct); ok { |
| return formatStructType(fset, decl, under) |
| } |
| } |
| return "", "", fmt.Errorf("no supported for %v, which is of type %T", obj.Name(), obj.Type()) |
| } |
| |
| func formatStructType(fset *token.FileSet, decl *ast.GenDecl, typ *types.Struct) (string, string, error) { |
| if len(decl.Specs) != 1 { |
| return "", "", fmt.Errorf("expected 1 TypeSpec got %v", len(decl.Specs)) |
| } |
| b := bytes.NewBuffer(nil) |
| if err := format.Node(b, fset, decl.Specs[0]); err != nil { |
| return "", "", err |
| } |
| doc := decl.Doc.Text() |
| return b.String(), doc, nil |
| |
| } |