1 // Invoke with //go:generate helper/helper -t Server -d protocol/tsserver.go -u lsp -o server_gen.go
2 // invoke in internal/lsp
21 typ = flag.String("t", "Server", "generate code for this type")
22 def = flag.String("d", "", "the file the type is defined in") // this relies on punning
23 use = flag.String("u", "", "look for uses in this package")
24 out = flag.String("o", "", "where to write the generated file")
28 log.SetFlags(log.Lshortfile)
30 if *typ == "" || *def == "" || *use == "" || *out == "" {
34 // read the type definition and see what methods we're looking for
37 // parse the package and see which methods are defined
43 // replace "\\\n" with nothing before using
47 // code generated by helper. DO NOT EDIT.
52 "golang.org/x/tools/internal/lsp/protocol"
55 {{range $key, $v := .Stuff}}
56 func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} {
57 {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\
58 {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}}
64 // put in empty param names as needed
65 for _, t := range types {
66 if t.paramnames == nil {
67 t.paramnames = make([]string, len(t.paramtypes))
69 for i, p := range t.paramtypes {
74 t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p)
75 t.Invoke += fmt.Sprintf("%s%s", cm, t.paramnames[i])
77 if len(t.Results) > 1 {
80 for i, r := range t.Results {
85 t.Result += fmt.Sprintf("%s%s", cm, r)
87 if len(t.Results) > 1 {
92 fd, err := os.Create(*out)
96 t, err := template.New("foo").Parse(tmpl)
104 p := par{*typ, types}
105 if false { // debugging the template
106 t.Execute(os.Stderr, &p)
108 buf := bytes.NewBuffer(nil)
109 err = t.Execute(buf, &p)
113 ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1))
121 fset := token.NewFileSet()
122 pkgs, err := parser.ParseDir(fset, *use, nil, 0)
124 log.Fatalf("%q:%v", *use, err)
126 pkg := pkgs["lsp"] // CHECK
128 for fname, f := range files {
129 for _, d := range f.Decls {
130 fd, ok := d.(*ast.FuncDecl)
134 nm := fd.Name.String()
135 if ast.IsExported(nm) {
136 // we're looking for things like didChange
139 if fx, ok := byname[nm]; ok {
141 log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname)
144 // and the Paramnames
146 for _, f := range ft.Params.List {
148 if len(f.Names) > 0 {
149 nm = f.Names[0].String()
151 fx.paramnames = append(fx.paramnames, nm)
157 for i, f := range types {
158 log.Printf("%d %s %s", i, f.Internal, f.Found)
163 type Function struct {
165 Internal string // first letter lower case
170 Result string // do it in code, easier than in a template
172 Found string // file it was found in
175 var types []*Function
176 var byname = map[string]*Function{} // internal names
179 fset := token.NewFileSet()
180 f, err := parser.ParseFile(fset, *def, nil, 0)
184 fd, err := os.Create("/tmp/ast")
188 ast.Fprint(fd, fset, f, ast.NotNilFilter)
189 ast.Inspect(f, inter)
190 sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name })
192 for i, f := range types {
193 log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results)
198 func inter(n ast.Node) bool {
199 x, ok := n.(*ast.TypeSpec)
200 if !ok || x.Name.Name != *typ {
203 m := x.Type.(*ast.InterfaceType).Methods.List
204 for _, fld := range m {
205 fn := fld.Type.(*ast.FuncType)
209 Name: fld.Names[0].String(),
211 fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:]
212 for _, f := range p {
213 fx.paramtypes = append(fx.paramtypes, whatis(f.Type))
215 for _, f := range r {
216 fx.Results = append(fx.Results, whatis(f.Type))
218 types = append(types, fx)
219 byname[fx.Internal] = fx
224 func whatis(x ast.Expr) string {
225 switch n := x.(type) {
226 case *ast.SelectorExpr:
227 return whatis(n.X) + "." + n.Sel.String()
229 return "*" + whatis(n.X)
231 if ast.IsExported(n.Name) {
232 // these are from package protocol
233 return "protocol." + n.Name
237 return "[]" + whatis(n.Elt)
238 case *ast.InterfaceType:
241 log.Fatalf("Fatal %T", x)
242 return fmt.Sprintf("%T", x)