1 // Copyright 2020 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.
5 // Invoke with //go:generate helper/helper -t Server -d protocol/tsserver.go -u lsp -o server_gen.go
6 // invoke in internal/lsp
25 typ = flag.String("t", "Server", "generate code for this type")
26 def = flag.String("d", "", "the file the type is defined in") // this relies on punning
27 use = flag.String("u", "", "look for uses in this package")
28 out = flag.String("o", "", "where to write the generated file")
32 log.SetFlags(log.Lshortfile)
34 if *typ == "" || *def == "" || *use == "" || *out == "" {
38 // read the type definition and see what methods we're looking for
41 // parse the package and see which methods are defined
47 // replace "\\\n" with nothing before using
51 // code generated by helper. DO NOT EDIT.
56 "golang.org/x/tools/internal/lsp/protocol"
59 {{range $key, $v := .Stuff}}
60 func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} {
61 {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\
62 {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}}
68 // put in empty param names as needed
69 for _, t := range types {
70 if t.paramnames == nil {
71 t.paramnames = make([]string, len(t.paramtypes))
73 for i, p := range t.paramtypes {
78 t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p)
79 this := t.paramnames[i]
83 t.Invoke += fmt.Sprintf("%s%s", cm, this)
85 if len(t.Results) > 1 {
88 for i, r := range t.Results {
93 t.Result += fmt.Sprintf("%s%s", cm, r)
95 if len(t.Results) > 1 {
100 fd, err := os.Create(*out)
104 t, err := template.New("foo").Parse(tmpl)
112 p := par{*typ, types}
113 if false { // debugging the template
114 t.Execute(os.Stderr, &p)
116 buf := bytes.NewBuffer(nil)
117 err = t.Execute(buf, &p)
121 ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1))
129 fset := token.NewFileSet()
130 pkgs, err := parser.ParseDir(fset, *use, nil, 0)
132 log.Fatalf("%q:%v", *use, err)
134 pkg := pkgs["lsp"] // CHECK
136 for fname, f := range files {
137 for _, d := range f.Decls {
138 fd, ok := d.(*ast.FuncDecl)
142 nm := fd.Name.String()
143 if ast.IsExported(nm) {
144 // we're looking for things like didChange
147 if fx, ok := byname[nm]; ok {
149 log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname)
152 // and the Paramnames
154 for _, f := range ft.Params.List {
156 if len(f.Names) > 0 {
157 nm = f.Names[0].String()
159 fx.paramnames = append(fx.paramnames, nm)
165 for i, f := range types {
166 log.Printf("%d %s %s", i, f.Internal, f.Found)
171 type Function struct {
173 Internal string // first letter lower case
178 Result string // do it in code, easier than in a template
180 Found string // file it was found in
183 var types []*Function
184 var byname = map[string]*Function{} // internal names
187 fset := token.NewFileSet()
188 f, err := parser.ParseFile(fset, *def, nil, 0)
192 fd, err := os.Create("/tmp/ast")
196 ast.Fprint(fd, fset, f, ast.NotNilFilter)
197 ast.Inspect(f, inter)
198 sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name })
200 for i, f := range types {
201 log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results)
206 func inter(n ast.Node) bool {
207 x, ok := n.(*ast.TypeSpec)
208 if !ok || x.Name.Name != *typ {
211 m := x.Type.(*ast.InterfaceType).Methods.List
212 for _, fld := range m {
213 fn := fld.Type.(*ast.FuncType)
217 Name: fld.Names[0].String(),
219 fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:]
220 for _, f := range p {
221 fx.paramtypes = append(fx.paramtypes, whatis(f.Type))
223 for _, f := range r {
224 fx.Results = append(fx.Results, whatis(f.Type))
226 types = append(types, fx)
227 byname[fx.Internal] = fx
232 func whatis(x ast.Expr) string {
233 switch n := x.(type) {
234 case *ast.SelectorExpr:
235 return whatis(n.X) + "." + n.Sel.String()
237 return "*" + whatis(n.X)
239 if ast.IsExported(n.Name) {
240 // these are from package protocol
241 return "protocol." + n.Name
245 return "[]" + whatis(n.Elt)
246 case *ast.InterfaceType:
249 log.Fatalf("Fatal %T", x)
250 return fmt.Sprintf("%T", x)