1// Copyright 2021 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 gen is used to generate command bindings from the gopls command 6// interface. 7package gen 8 9import ( 10 "bytes" 11 "fmt" 12 "go/types" 13 "text/template" 14 15 "golang.org/x/tools/internal/imports" 16 "golang.org/x/tools/internal/lsp/command/commandmeta" 17) 18 19const src = `// Copyright 2021 The Go Authors. All rights reserved. 20// Use of this source code is governed by a BSD-style 21// license that can be found in the LICENSE file. 22 23// Don't include this file during code generation, or it will break the build 24// if existing interface methods have been modified. 25//go:build !generate 26// +build !generate 27 28package command 29 30// Code generated by generate.go. DO NOT EDIT. 31 32import ( 33 {{range $k, $v := .Imports -}} 34 "{{$k}}" 35 {{end}} 36) 37 38const ( 39{{- range .Commands}} 40 {{.MethodName}} Command = "{{.Name}}" 41{{- end}} 42) 43 44var Commands = []Command { 45{{- range .Commands}} 46 {{.MethodName}}, 47{{- end}} 48} 49 50func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Interface) (interface{}, error) { 51 switch params.Command { 52 {{- range .Commands}} 53 case "{{.ID}}": 54 {{- if .Args -}} 55 {{- range $i, $v := .Args}} 56 var a{{$i}} {{typeString $v.Type}} 57 {{- end}} 58 if err := UnmarshalArgs(params.Arguments{{range $i, $v := .Args}}, &a{{$i}}{{end}}); err != nil { 59 return nil, err 60 } 61 {{end -}} 62 return {{if not .Result}}nil, {{end}}s.{{.MethodName}}(ctx{{range $i, $v := .Args}}, a{{$i}}{{end}}) 63 {{- end}} 64 } 65 return nil, fmt.Errorf("unsupported command %q", params.Command) 66} 67{{- range .Commands}} 68 69func New{{.MethodName}}Command(title string, {{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}} {{typeString $v.Type}}{{end}}) (protocol.Command, error) { 70 args, err := MarshalArgs({{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}}{{end}}) 71 if err != nil { 72 return protocol.Command{}, err 73 } 74 return protocol.Command{ 75 Title: title, 76 Command: "{{.ID}}", 77 Arguments: args, 78 }, nil 79} 80{{end}} 81` 82 83type data struct { 84 Imports map[string]bool 85 Commands []*commandmeta.Command 86} 87 88func Generate() ([]byte, error) { 89 pkg, cmds, err := commandmeta.Load() 90 if err != nil { 91 return nil, fmt.Errorf("loading command data: %v", err) 92 } 93 qf := func(p *types.Package) string { 94 if p == pkg.Types { 95 return "" 96 } 97 return p.Name() 98 } 99 tmpl, err := template.New("").Funcs(template.FuncMap{ 100 "typeString": func(t types.Type) string { 101 return types.TypeString(t, qf) 102 }, 103 }).Parse(src) 104 if err != nil { 105 return nil, err 106 } 107 d := data{ 108 Commands: cmds, 109 Imports: map[string]bool{ 110 "context": true, 111 "fmt": true, 112 "golang.org/x/tools/internal/lsp/protocol": true, 113 }, 114 } 115 const thispkg = "golang.org/x/tools/internal/lsp/command" 116 for _, c := range d.Commands { 117 for _, arg := range c.Args { 118 pth := pkgPath(arg.Type) 119 if pth != "" && pth != thispkg { 120 d.Imports[pth] = true 121 } 122 } 123 pth := pkgPath(c.Result) 124 if pth != "" && pth != thispkg { 125 d.Imports[pth] = true 126 } 127 } 128 129 var buf bytes.Buffer 130 if err := tmpl.Execute(&buf, d); err != nil { 131 return nil, fmt.Errorf("executing: %v", err) 132 } 133 134 opts := &imports.Options{ 135 AllErrors: true, 136 FormatOnly: true, 137 Comments: true, 138 } 139 content, err := imports.Process("", buf.Bytes(), opts) 140 if err != nil { 141 return nil, fmt.Errorf("goimports: %v", err) 142 } 143 return content, nil 144} 145 146func pkgPath(t types.Type) string { 147 if n, ok := t.(*types.Named); ok { 148 if pkg := n.Obj().Pkg(); pkg != nil { 149 return pkg.Path() 150 } 151 } 152 return "" 153} 154