blob: 3934f1adbc8c39a659c4d56510926f76e3b87c88 [file] [log] [blame]
// 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"
"text/template"
"golang.org/x/tools/internal/imports"
"golang.org/x/tools/internal/lsp/command/commandmeta"
)
const src = `// 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.
// 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
package command
// Code generated by generate.go. DO NOT EDIT.
import (
{{range $k, $v := .Imports -}}
"{{$k}}"
{{end}}
)
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 params.Command {
{{- range .Commands}}
case "{{.ID}}":
{{- 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}}
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 protocol.Command{}, err
}
return protocol.Command{
Title: title,
Command: "{{.ID}}",
Arguments: args,
}, nil
}
{{end}}
`
type data struct {
Imports map[string]bool
Commands []*commandmeta.Command
}
func Generate() ([]byte, error) {
pkg, cmds, err := commandmeta.Load()
if err != nil {
return nil, fmt.Errorf("loading command data: %v", err)
}
qf := func(p *types.Package) string {
if p == pkg.Types {
return ""
}
return p.Name()
}
tmpl, err := template.New("").Funcs(template.FuncMap{
"typeString": func(t types.Type) string {
return types.TypeString(t, qf)
},
}).Parse(src)
if err != nil {
return nil, err
}
d := data{
Commands: cmds,
Imports: map[string]bool{
"context": true,
"fmt": true,
"golang.org/x/tools/internal/lsp/protocol": true,
},
}
const thispkg = "golang.org/x/tools/internal/lsp/command"
for _, c := range d.Commands {
for _, arg := range c.Args {
pth := pkgPath(arg.Type)
if pth != "" && pth != thispkg {
d.Imports[pth] = true
}
}
pth := pkgPath(c.Result)
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 {
if n, ok := t.(*types.Named); ok {
if pkg := n.Obj().Pkg(); pkg != nil {
return pkg.Path()
}
}
return ""
}