+++ /dev/null
-// Invoke with //go:generate helper/helper -t Server -d protocol/tsserver.go -u lsp -o server_gen.go
-// invoke in internal/lsp
-package main
-
-import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/format"
- "go/parser"
- "go/token"
- "log"
- "os"
- "sort"
- "strings"
- "text/template"
-)
-
-var (
- typ = flag.String("t", "Server", "generate code for this type")
- def = flag.String("d", "", "the file the type is defined in") // this relies on punning
- use = flag.String("u", "", "look for uses in this package")
- out = flag.String("o", "", "where to write the generated file")
-)
-
-func main() {
- log.SetFlags(log.Lshortfile)
- flag.Parse()
- if *typ == "" || *def == "" || *use == "" || *out == "" {
- flag.PrintDefaults()
- return
- }
- // read the type definition and see what methods we're looking for
- doTypes()
-
- // parse the package and see which methods are defined
- doUses()
-
- output()
-}
-
-// replace "\\\n" with nothing before using
-var tmpl = `
-package lsp
-
-// code generated by helper. DO NOT EDIT.
-
-import (
- "context"
-
- "golang.org/x/tools/internal/lsp/protocol"
-)
-
-{{range $key, $v := .Stuff}}
-func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} {
- {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\
- {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}}
-}
-{{end}}
-`
-
-func output() {
- // put in empty param names as needed
- for _, t := range types {
- if t.paramnames == nil {
- t.paramnames = make([]string, len(t.paramtypes))
- }
- for i, p := range t.paramtypes {
- cm := ""
- if i > 0 {
- cm = ", "
- }
- t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p)
- t.Invoke += fmt.Sprintf("%s%s", cm, t.paramnames[i])
- }
- if len(t.Results) > 1 {
- t.Result = "("
- }
- for i, r := range t.Results {
- cm := ""
- if i > 0 {
- cm = ", "
- }
- t.Result += fmt.Sprintf("%s%s", cm, r)
- }
- if len(t.Results) > 1 {
- t.Result += ")"
- }
- }
-
- fd, err := os.Create(*out)
- if err != nil {
- log.Fatal(err)
- }
- t, err := template.New("foo").Parse(tmpl)
- if err != nil {
- log.Fatal(err)
- }
- type par struct {
- Type string
- Stuff []*Function
- }
- p := par{*typ, types}
- if false { // debugging the template
- t.Execute(os.Stderr, &p)
- }
- buf := bytes.NewBuffer(nil)
- err = t.Execute(buf, &p)
- if err != nil {
- log.Fatal(err)
- }
- ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1))
- if err != nil {
- log.Fatal(err)
- }
- fd.Write(ans)
-}
-
-func doUses() {
- fset := token.NewFileSet()
- pkgs, err := parser.ParseDir(fset, *use, nil, 0)
- if err != nil {
- log.Fatalf("%q:%v", *use, err)
- }
- pkg := pkgs["lsp"] // CHECK
- files := pkg.Files
- for fname, f := range files {
- for _, d := range f.Decls {
- fd, ok := d.(*ast.FuncDecl)
- if !ok {
- continue
- }
- nm := fd.Name.String()
- if ast.IsExported(nm) {
- // we're looking for things like didChange
- continue
- }
- if fx, ok := byname[nm]; ok {
- if fx.Found != "" {
- log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname)
- }
- fx.Found = fname
- // and the Paramnames
- ft := fd.Type
- for _, f := range ft.Params.List {
- nm := ""
- if len(f.Names) > 0 {
- nm = f.Names[0].String()
- }
- fx.paramnames = append(fx.paramnames, nm)
- }
- }
- }
- }
- if false {
- for i, f := range types {
- log.Printf("%d %s %s", i, f.Internal, f.Found)
- }
- }
-}
-
-type Function struct {
- Name string
- Internal string // first letter lower case
- paramtypes []string
- paramnames []string
- Results []string
- Param string
- Result string // do it in code, easier than in a template
- Invoke string
- Found string // file it was found in
-}
-
-var types []*Function
-var byname = map[string]*Function{} // internal names
-
-func doTypes() {
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, *def, nil, 0)
- if err != nil {
- log.Fatal(err)
- }
- fd, err := os.Create("/tmp/ast")
- if err != nil {
- log.Fatal(err)
- }
- ast.Fprint(fd, fset, f, ast.NotNilFilter)
- ast.Inspect(f, inter)
- sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name })
- if false {
- for i, f := range types {
- log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results)
- }
- }
-}
-
-func inter(n ast.Node) bool {
- x, ok := n.(*ast.TypeSpec)
- if !ok || x.Name.Name != *typ {
- return true
- }
- m := x.Type.(*ast.InterfaceType).Methods.List
- for _, fld := range m {
- fn := fld.Type.(*ast.FuncType)
- p := fn.Params.List
- r := fn.Results.List
- fx := &Function{
- Name: fld.Names[0].String(),
- }
- fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:]
- for _, f := range p {
- fx.paramtypes = append(fx.paramtypes, whatis(f.Type))
- }
- for _, f := range r {
- fx.Results = append(fx.Results, whatis(f.Type))
- }
- types = append(types, fx)
- byname[fx.Internal] = fx
- }
- return false
-}
-
-func whatis(x ast.Expr) string {
- switch n := x.(type) {
- case *ast.SelectorExpr:
- return whatis(n.X) + "." + n.Sel.String()
- case *ast.StarExpr:
- return "*" + whatis(n.X)
- case *ast.Ident:
- if ast.IsExported(n.Name) {
- // these are from package protocol
- return "protocol." + n.Name
- }
- return n.Name
- case *ast.ArrayType:
- return "[]" + whatis(n.Elt)
- case *ast.InterfaceType:
- return "interface{}"
- default:
- log.Fatalf("Fatal %T", x)
- return fmt.Sprintf("%T", x)
- }
-}