| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package gen is used to generate command bindings from the gopls command |
| // interface. |
| package gen |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/types" |
| "log" |
| "text/template" |
| |
| "golang.org/x/tools/gopls/internal/protocol/command/commandmeta" |
| "golang.org/x/tools/internal/imports" |
| ) |
| |
| const src = `// Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Don't include this file during code generation, or it will break the build |
| // if existing interface methods have been modified. |
| //go:build !generate |
| // +build !generate |
| |
| // Code generated by gen.go from gopls/internal/protocol/command. DO NOT EDIT. |
| |
| package command |
| |
| import ( |
| {{range $k, $v := .Imports -}} |
| "{{$k}}" |
| {{end}} |
| ) |
| |
| // Symbolic names for gopls commands, corresponding to methods of [Interface]. |
| // |
| // The string value is used in the Command field of protocol.Command. |
| // These commands may be obtained from a CodeLens or CodeAction request |
| // and executed by an ExecuteCommand request. |
| const ( |
| {{- range .Commands}} |
| {{.MethodName}} Command = "{{.Name}}" |
| {{- end}} |
| ) |
| |
| var Commands = []Command { |
| {{- range .Commands}} |
| {{.MethodName}}, |
| {{- end}} |
| } |
| |
| func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Interface) (interface{}, error) { |
| switch Command(params.Command) { |
| {{- range .Commands}} |
| case {{.MethodName}}: |
| {{- if .Args -}} |
| {{- range $i, $v := .Args}} |
| var a{{$i}} {{typeString $v.Type}} |
| {{- end}} |
| if err := UnmarshalArgs(params.Arguments{{range $i, $v := .Args}}, &a{{$i}}{{end}}); err != nil { |
| return nil, err |
| } |
| {{end -}} |
| return {{if not .Result}}nil, {{end}}s.{{.MethodName}}(ctx{{range $i, $v := .Args}}, a{{$i}}{{end}}) |
| {{- end}} |
| } |
| return nil, fmt.Errorf("unsupported command %q", params.Command) |
| } |
| {{- range .Commands}} |
| |
| {{if fallible .Args}} |
| func New{{.MethodName}}Command(title string, {{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}} {{typeString $v.Type}}{{end}}) (*protocol.Command, error) { |
| args, err := MarshalArgs({{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}}{{end}}) |
| if err != nil { |
| return nil, err |
| } |
| return &protocol.Command{ |
| Title: title, |
| Command: {{.MethodName}}.String(), |
| Arguments: args, |
| }, nil |
| } |
| {{else}} |
| func New{{.MethodName}}Command(title string, {{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}} {{typeString $v.Type}}{{end}}) *protocol.Command { |
| return &protocol.Command{ |
| Title: title, |
| Command: {{.MethodName}}.String(), |
| Arguments: MustMarshalArgs({{range $i, $v := .Args}}{{if $i}}, {{end}}a{{$i}}{{end}}), |
| } |
| } |
| {{end}} |
| |
| {{end}} |
| ` |
| |
| type data struct { |
| Imports map[string]bool |
| Commands []*commandmeta.Command |
| } |
| |
| // Generate computes the new contents of ../command_gen.go from a |
| // static analysis of the command.Interface type. |
| func Generate() ([]byte, error) { |
| cmds, err := commandmeta.Load() |
| if err != nil { |
| return nil, fmt.Errorf("loading command data: %v", err) |
| } |
| const thispkg = "golang.org/x/tools/gopls/internal/protocol/command" |
| qf := func(p *types.Package) string { |
| if p.Path() == thispkg { |
| return "" |
| } |
| return p.Name() |
| } |
| tmpl, err := template.New("").Funcs(template.FuncMap{ |
| "typeString": func(t types.Type) string { |
| return types.TypeString(t, qf) |
| }, |
| "fallible": func(args []*commandmeta.Field) bool { |
| var fallible func(types.Type) bool |
| fallible = func(t types.Type) bool { |
| switch t := t.Underlying().(type) { |
| case *types.Basic: |
| return false |
| case *types.Slice: |
| return fallible(t.Elem()) |
| case *types.Struct: |
| for i := 0; i < t.NumFields(); i++ { |
| if fallible(t.Field(i).Type()) { |
| return true |
| } |
| } |
| return false |
| } |
| // Assume all other types are fallible for now: |
| log.Println("Command.Args has fallible type", t) |
| return true |
| } |
| for _, arg := range args { |
| if fallible(arg.Type) { |
| return true |
| } |
| } |
| return false |
| }, |
| }).Parse(src) |
| if err != nil { |
| return nil, err |
| } |
| d := data{ |
| Commands: cmds, |
| Imports: map[string]bool{ |
| "context": true, |
| "fmt": true, |
| "golang.org/x/tools/gopls/internal/protocol": true, |
| }, |
| } |
| for _, c := range d.Commands { |
| for _, arg := range c.Args { |
| pth := pkgPath(arg.Type) |
| if pth != "" && pth != thispkg { |
| d.Imports[pth] = true |
| } |
| } |
| if c.Result != nil { |
| pth := pkgPath(c.Result.Type) |
| if pth != "" && pth != thispkg { |
| d.Imports[pth] = true |
| } |
| } |
| } |
| |
| var buf bytes.Buffer |
| if err := tmpl.Execute(&buf, d); err != nil { |
| return nil, fmt.Errorf("executing: %v", err) |
| } |
| |
| opts := &imports.Options{ |
| AllErrors: true, |
| FormatOnly: true, |
| Comments: true, |
| } |
| content, err := imports.Process("", buf.Bytes(), opts) |
| if err != nil { |
| return nil, fmt.Errorf("goimports: %v", err) |
| } |
| return content, nil |
| } |
| |
| func pkgPath(t types.Type) string { |
| type hasTypeName interface { // *Named or *Alias (or *TypeParam) |
| Obj() *types.TypeName |
| } |
| if t, ok := t.(hasTypeName); ok { |
| if pkg := t.Obj().Pkg(); pkg != nil { |
| return pkg.Path() |
| } |
| } |
| return "" |
| } |