github.com/anthonyme00/gomarkdoc@v1.0.0/lang/symbol.go (about)

     1  package lang
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/doc"
     7  	"strings"
     8  )
     9  
    10  type (
    11  	// Symbol provides identity information for a symbol in a package.
    12  	Symbol struct {
    13  		// Receiver holds the receiver for a method or field.
    14  		Receiver string
    15  		// Name holds the name of the symbol itself.
    16  		Name string
    17  		// Kind identifies the category of the symbol.
    18  		Kind SymbolKind
    19  		// Parent holds the linkable parent symbol which contains this one.
    20  		Parent *Symbol
    21  	}
    22  
    23  	// SymbolKind identifies the type of symbol.
    24  	SymbolKind int
    25  )
    26  
    27  // The list of valid symbol kinds.
    28  const (
    29  	TypeSymbolKind SymbolKind = iota + 1
    30  	FuncSymbolKind
    31  	ConstSymbolKind
    32  	VarSymbolKind
    33  	MethodSymbolKind
    34  	FieldSymbolKind
    35  )
    36  
    37  // PackageSymbols gets the list of symbols for a doc package.
    38  func PackageSymbols(pkg *doc.Package) map[string]Symbol {
    39  	sym := make(map[string]Symbol)
    40  	for _, c := range pkg.Consts {
    41  		parent := Symbol{
    42  			Name: c.Names[0],
    43  			Kind: ConstSymbolKind,
    44  		}
    45  
    46  		for _, n := range c.Names {
    47  			sym[symbolName("", n)] = Symbol{
    48  				Name:   n,
    49  				Kind:   ConstSymbolKind,
    50  				Parent: &parent,
    51  			}
    52  		}
    53  	}
    54  
    55  	for _, v := range pkg.Vars {
    56  		parent := Symbol{
    57  			Name: v.Names[0],
    58  			Kind: VarSymbolKind,
    59  		}
    60  
    61  		for _, n := range v.Names {
    62  			sym[symbolName("", n)] = Symbol{
    63  				Name:   n,
    64  				Kind:   VarSymbolKind,
    65  				Parent: &parent,
    66  			}
    67  		}
    68  	}
    69  
    70  	for _, v := range pkg.Funcs {
    71  		sym[symbolName("", v.Name)] = Symbol{
    72  			Name: v.Name,
    73  			Kind: FuncSymbolKind,
    74  		}
    75  	}
    76  
    77  	for _, t := range pkg.Types {
    78  		typeSymbols(sym, t)
    79  	}
    80  
    81  	return sym
    82  }
    83  
    84  func typeSymbols(sym map[string]Symbol, t *doc.Type) {
    85  	typeSym := Symbol{
    86  		Name: t.Name,
    87  		Kind: TypeSymbolKind,
    88  	}
    89  
    90  	sym[t.Name] = typeSym
    91  
    92  	for _, f := range t.Methods {
    93  		sym[symbolName(t.Name, f.Name)] = Symbol{
    94  			Receiver: t.Name,
    95  			Name:     f.Name,
    96  			Kind:     MethodSymbolKind,
    97  		}
    98  	}
    99  
   100  	for _, s := range t.Decl.Specs {
   101  		typ, ok := s.(*ast.TypeSpec).Type.(*ast.StructType)
   102  		if !ok {
   103  			continue
   104  		}
   105  
   106  		for _, f := range typ.Fields.List {
   107  			for _, n := range f.Names {
   108  				sym[symbolName(t.Name, n.String())] = Symbol{
   109  					Receiver: t.Name,
   110  					Name:     n.String(),
   111  					Kind:     FieldSymbolKind,
   112  					Parent:   &typeSym,
   113  				}
   114  			}
   115  		}
   116  	}
   117  
   118  	for _, f := range t.Funcs {
   119  		sym[symbolName("", f.Name)] = Symbol{
   120  			Name: f.Name,
   121  			Kind: FuncSymbolKind,
   122  		}
   123  	}
   124  
   125  	for _, c := range t.Consts {
   126  		parent := Symbol{
   127  			Name: c.Names[0],
   128  			Kind: ConstSymbolKind,
   129  		}
   130  
   131  		for _, n := range c.Names {
   132  			sym[symbolName("", n)] = Symbol{
   133  				Name:   n,
   134  				Kind:   ConstSymbolKind,
   135  				Parent: &parent,
   136  			}
   137  		}
   138  	}
   139  
   140  	for _, v := range t.Vars {
   141  		parent := Symbol{
   142  			Name: v.Names[0],
   143  			Kind: VarSymbolKind,
   144  		}
   145  
   146  		for _, n := range v.Names {
   147  			sym[symbolName("", n)] = Symbol{
   148  				Name:   n,
   149  				Kind:   VarSymbolKind,
   150  				Parent: &parent,
   151  			}
   152  		}
   153  	}
   154  
   155  }
   156  
   157  // Anchor produces anchor text for the symbol.
   158  func (s Symbol) Anchor() string {
   159  	if s.Parent != nil {
   160  		return s.Parent.Anchor()
   161  	}
   162  
   163  	switch s.Kind {
   164  	case MethodSymbolKind, FieldSymbolKind:
   165  		return fmt.Sprintf("%s.%s", strings.TrimLeft(s.Receiver, "*"), s.Name)
   166  	default:
   167  		return s.Name
   168  	}
   169  }
   170  
   171  // symbolName returns the string representation of the symbol.
   172  func symbolName(receiver string, name string) string {
   173  	receiver = strings.TrimLeft(receiver, "*")
   174  	name = strings.TrimLeft(name, "*")
   175  
   176  	if receiver == "" {
   177  		return name
   178  	}
   179  
   180  	return fmt.Sprintf("%s.%s", receiver, name)
   181  }