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 5package cmd 6 7import ( 8 "context" 9 "encoding/json" 10 "flag" 11 "fmt" 12 "sort" 13 14 "golang.org/x/tools/internal/lsp/protocol" 15 "golang.org/x/tools/internal/span" 16 "golang.org/x/tools/internal/tool" 17) 18 19// symbols implements the symbols verb for gopls 20type symbols struct { 21 app *Application 22} 23 24func (r *symbols) Name() string { return "symbols" } 25func (r *symbols) Usage() string { return "<file>" } 26func (r *symbols) ShortHelp() string { return "display selected file's symbols" } 27func (r *symbols) DetailedHelp(f *flag.FlagSet) { 28 fmt.Fprint(f.Output(), ` 29Example: 30 $ gopls symbols helper/helper.go 31`) 32 f.PrintDefaults() 33} 34func (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 72func 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 93func 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 104func printSymbolInformation(s protocol.SymbolInformation) { 105 fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.Location.Range)) 106} 107 108func 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} 116