github.com/v2fly/tools@v0.100.0/internal/lsp/source/symbols.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package source
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/types"
    12  
    13  	"github.com/v2fly/tools/internal/event"
    14  	"github.com/v2fly/tools/internal/lsp/protocol"
    15  	errors "golang.org/x/xerrors"
    16  )
    17  
    18  func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.DocumentSymbol, error) {
    19  	ctx, done := event.Start(ctx, "source.DocumentSymbols")
    20  	defer done()
    21  
    22  	pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
    23  	if err != nil {
    24  		return nil, errors.Errorf("getting file for DocumentSymbols: %w", err)
    25  	}
    26  
    27  	info := pkg.GetTypesInfo()
    28  	q := Qualifier(pgf.File, pkg.GetTypes(), info)
    29  
    30  	symbolsToReceiver := make(map[types.Type]int)
    31  	var symbols []protocol.DocumentSymbol
    32  	for _, decl := range pgf.File.Decls {
    33  		switch decl := decl.(type) {
    34  		case *ast.FuncDecl:
    35  			if decl.Name.Name == "_" {
    36  				continue
    37  			}
    38  			if obj := info.ObjectOf(decl.Name); obj != nil {
    39  				fs, err := funcSymbol(snapshot, pkg, decl, obj, q)
    40  				if err != nil {
    41  					return nil, err
    42  				}
    43  				// If function is a method, prepend the type of the method.
    44  				if fs.Kind == protocol.Method {
    45  					rtype := obj.Type().(*types.Signature).Recv().Type()
    46  					fs.Name = fmt.Sprintf("(%s).%s", types.TypeString(rtype, q), fs.Name)
    47  				}
    48  				symbols = append(symbols, fs)
    49  			}
    50  		case *ast.GenDecl:
    51  			for _, spec := range decl.Specs {
    52  				switch spec := spec.(type) {
    53  				case *ast.TypeSpec:
    54  					if spec.Name.Name == "_" {
    55  						continue
    56  					}
    57  					if obj := info.ObjectOf(spec.Name); obj != nil {
    58  						ts, err := typeSymbol(snapshot, pkg, info, spec, obj, q)
    59  						if err != nil {
    60  							return nil, err
    61  						}
    62  						symbols = append(symbols, ts)
    63  						symbolsToReceiver[obj.Type()] = len(symbols) - 1
    64  					}
    65  				case *ast.ValueSpec:
    66  					for _, name := range spec.Names {
    67  						if name.Name == "_" {
    68  							continue
    69  						}
    70  						if obj := info.ObjectOf(name); obj != nil {
    71  							vs, err := varSymbol(snapshot, pkg, decl, name, obj, q)
    72  							if err != nil {
    73  								return nil, err
    74  							}
    75  							symbols = append(symbols, vs)
    76  						}
    77  					}
    78  				}
    79  			}
    80  		}
    81  	}
    82  	return symbols, nil
    83  }
    84  
    85  func funcSymbol(snapshot Snapshot, pkg Package, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
    86  	s := protocol.DocumentSymbol{
    87  		Name: obj.Name(),
    88  		Kind: protocol.Function,
    89  	}
    90  	var err error
    91  	s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
    92  	if err != nil {
    93  		return protocol.DocumentSymbol{}, err
    94  	}
    95  	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, decl.Name)
    96  	if err != nil {
    97  		return protocol.DocumentSymbol{}, err
    98  	}
    99  	sig, _ := obj.Type().(*types.Signature)
   100  	if sig != nil {
   101  		if sig.Recv() != nil {
   102  			s.Kind = protocol.Method
   103  		}
   104  		s.Detail += "("
   105  		for i := 0; i < sig.Params().Len(); i++ {
   106  			if i > 0 {
   107  				s.Detail += ", "
   108  			}
   109  			param := sig.Params().At(i)
   110  			label := types.TypeString(param.Type(), q)
   111  			if param.Name() != "" {
   112  				label = fmt.Sprintf("%s %s", param.Name(), label)
   113  			}
   114  			s.Detail += label
   115  		}
   116  		s.Detail += ")"
   117  	}
   118  	return s, nil
   119  }
   120  
   121  func typeSymbol(snapshot Snapshot, pkg Package, info *types.Info, spec *ast.TypeSpec, obj types.Object, qf types.Qualifier) (protocol.DocumentSymbol, error) {
   122  	s := protocol.DocumentSymbol{
   123  		Name: obj.Name(),
   124  	}
   125  	s.Detail, _ = FormatType(obj.Type(), qf)
   126  	s.Kind = typeToKind(obj.Type())
   127  
   128  	var err error
   129  	s.Range, err = nodeToProtocolRange(snapshot, pkg, spec)
   130  	if err != nil {
   131  		return protocol.DocumentSymbol{}, err
   132  	}
   133  	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, spec.Name)
   134  	if err != nil {
   135  		return protocol.DocumentSymbol{}, err
   136  	}
   137  	t, objIsStruct := obj.Type().Underlying().(*types.Struct)
   138  	st, specIsStruct := spec.Type.(*ast.StructType)
   139  	if objIsStruct && specIsStruct {
   140  		for i := 0; i < t.NumFields(); i++ {
   141  			f := t.Field(i)
   142  			child := protocol.DocumentSymbol{
   143  				Name: f.Name(),
   144  				Kind: protocol.Field,
   145  			}
   146  			child.Detail, _ = FormatType(f.Type(), qf)
   147  
   148  			spanNode, selectionNode := nodesForStructField(i, st)
   149  			if span, err := nodeToProtocolRange(snapshot, pkg, spanNode); err == nil {
   150  				child.Range = span
   151  			}
   152  			if span, err := nodeToProtocolRange(snapshot, pkg, selectionNode); err == nil {
   153  				child.SelectionRange = span
   154  			}
   155  			s.Children = append(s.Children, child)
   156  		}
   157  	}
   158  
   159  	ti, objIsInterface := obj.Type().Underlying().(*types.Interface)
   160  	ai, specIsInterface := spec.Type.(*ast.InterfaceType)
   161  	if objIsInterface && specIsInterface {
   162  		for i := 0; i < ti.NumExplicitMethods(); i++ {
   163  			method := ti.ExplicitMethod(i)
   164  			child := protocol.DocumentSymbol{
   165  				Name: method.Name(),
   166  				Kind: protocol.Method,
   167  			}
   168  
   169  			var spanNode, selectionNode ast.Node
   170  		Methods:
   171  			for _, f := range ai.Methods.List {
   172  				for _, id := range f.Names {
   173  					if id.Name == method.Name() {
   174  						spanNode, selectionNode = f, id
   175  						break Methods
   176  					}
   177  				}
   178  			}
   179  			child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
   180  			if err != nil {
   181  				return protocol.DocumentSymbol{}, err
   182  			}
   183  			child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
   184  			if err != nil {
   185  				return protocol.DocumentSymbol{}, err
   186  			}
   187  			s.Children = append(s.Children, child)
   188  		}
   189  
   190  		for i := 0; i < ti.NumEmbeddeds(); i++ {
   191  			embedded := ti.EmbeddedType(i)
   192  			nt, isNamed := embedded.(*types.Named)
   193  			if !isNamed {
   194  				continue
   195  			}
   196  
   197  			child := protocol.DocumentSymbol{
   198  				Name: types.TypeString(embedded, qf),
   199  			}
   200  			child.Kind = typeToKind(embedded)
   201  			var spanNode, selectionNode ast.Node
   202  		Embeddeds:
   203  			for _, f := range ai.Methods.List {
   204  				if len(f.Names) > 0 {
   205  					continue
   206  				}
   207  
   208  				if t := info.TypeOf(f.Type); types.Identical(nt, t) {
   209  					spanNode, selectionNode = f, f.Type
   210  					break Embeddeds
   211  				}
   212  			}
   213  			child.Range, err = nodeToProtocolRange(snapshot, pkg, spanNode)
   214  			if err != nil {
   215  				return protocol.DocumentSymbol{}, err
   216  			}
   217  			child.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, selectionNode)
   218  			if err != nil {
   219  				return protocol.DocumentSymbol{}, err
   220  			}
   221  			s.Children = append(s.Children, child)
   222  		}
   223  	}
   224  	return s, nil
   225  }
   226  
   227  func nodesForStructField(i int, st *ast.StructType) (span, selection ast.Node) {
   228  	j := 0
   229  	for _, field := range st.Fields.List {
   230  		if len(field.Names) == 0 {
   231  			if i == j {
   232  				return field, field.Type
   233  			}
   234  			j++
   235  			continue
   236  		}
   237  		for _, name := range field.Names {
   238  			if i == j {
   239  				return field, name
   240  			}
   241  			j++
   242  		}
   243  	}
   244  	return nil, nil
   245  }
   246  
   247  func varSymbol(snapshot Snapshot, pkg Package, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) (protocol.DocumentSymbol, error) {
   248  	s := protocol.DocumentSymbol{
   249  		Name: obj.Name(),
   250  		Kind: protocol.Variable,
   251  	}
   252  	if _, ok := obj.(*types.Const); ok {
   253  		s.Kind = protocol.Constant
   254  	}
   255  	var err error
   256  	s.Range, err = nodeToProtocolRange(snapshot, pkg, decl)
   257  	if err != nil {
   258  		return protocol.DocumentSymbol{}, err
   259  	}
   260  	s.SelectionRange, err = nodeToProtocolRange(snapshot, pkg, name)
   261  	if err != nil {
   262  		return protocol.DocumentSymbol{}, err
   263  	}
   264  	s.Detail = types.TypeString(obj.Type(), q)
   265  	return s, nil
   266  }