github.com/v2fly/tools@v0.100.0/internal/lsp/cmd/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 cmd
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"flag"
    11  	"fmt"
    12  	"sort"
    13  
    14  	"github.com/v2fly/tools/internal/lsp/protocol"
    15  	"github.com/v2fly/tools/internal/span"
    16  	"github.com/v2fly/tools/internal/tool"
    17  )
    18  
    19  // symbols implements the symbols verb for gopls
    20  type symbols struct {
    21  	app *Application
    22  }
    23  
    24  func (r *symbols) Name() string      { return "symbols" }
    25  func (r *symbols) Usage() string     { return "<file>" }
    26  func (r *symbols) ShortHelp() string { return "display selected file's symbols" }
    27  func (r *symbols) DetailedHelp(f *flag.FlagSet) {
    28  	fmt.Fprint(f.Output(), `
    29  Example:
    30    $ gopls symbols helper/helper.go
    31  `)
    32  	f.PrintDefaults()
    33  }
    34  func (r *symbols) Run(ctx context.Context, args ...string) error {
    35  	if len(args) != 1 {
    36  		return tool.CommandLineErrorf("symbols expects 1 argument (position)")
    37  	}
    38  
    39  	conn, err := r.app.connect(ctx)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	defer conn.terminate(ctx)
    44  
    45  	from := span.Parse(args[0])
    46  	p := protocol.DocumentSymbolParams{
    47  		TextDocument: protocol.TextDocumentIdentifier{
    48  			URI: protocol.URIFromSpanURI(from.URI()),
    49  		},
    50  	}
    51  	symbols, err := conn.DocumentSymbol(ctx, &p)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	for _, s := range symbols {
    56  		if m, ok := s.(map[string]interface{}); ok {
    57  			s, err = mapToSymbol(m)
    58  			if err != nil {
    59  				return err
    60  			}
    61  		}
    62  		switch t := s.(type) {
    63  		case protocol.DocumentSymbol:
    64  			printDocumentSymbol(t)
    65  		case protocol.SymbolInformation:
    66  			printSymbolInformation(t)
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  func mapToSymbol(m map[string]interface{}) (interface{}, error) {
    73  	b, err := json.Marshal(m)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	if _, ok := m["selectionRange"]; ok {
    79  		var s protocol.DocumentSymbol
    80  		if err := json.Unmarshal(b, &s); err != nil {
    81  			return nil, err
    82  		}
    83  		return s, nil
    84  	}
    85  
    86  	var s protocol.SymbolInformation
    87  	if err := json.Unmarshal(b, &s); err != nil {
    88  		return nil, err
    89  	}
    90  	return s, nil
    91  }
    92  
    93  func printDocumentSymbol(s protocol.DocumentSymbol) {
    94  	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.SelectionRange))
    95  	// Sort children for consistency
    96  	sort.Slice(s.Children, func(i, j int) bool {
    97  		return s.Children[i].Name < s.Children[j].Name
    98  	})
    99  	for _, c := range s.Children {
   100  		fmt.Printf("\t%s %s %s\n", c.Name, c.Kind, positionToString(c.SelectionRange))
   101  	}
   102  }
   103  
   104  func printSymbolInformation(s protocol.SymbolInformation) {
   105  	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.Location.Range))
   106  }
   107  
   108  func positionToString(r protocol.Range) string {
   109  	return fmt.Sprintf("%v:%v-%v:%v",
   110  		r.Start.Line+1,
   111  		r.Start.Character+1,
   112  		r.End.Line+1,
   113  		r.End.Character+1,
   114  	)
   115  }