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 this := t.paramnames[i]
79 t.Invoke += fmt.Sprintf("%s%s", cm, this)
81 if len(t.Results) > 1 {
84 for i, r := range t.Results {
89 t.Result += fmt.Sprintf("%s%s", cm, r)
91 if len(t.Results) > 1 {
96 fd, err := os.Create(*out)
100 t, err := template.New("foo").Parse(tmpl)
108 p := par{*typ, types}
109 if false { // debugging the template
110 t.Execute(os.Stderr, &p)
112 buf := bytes.NewBuffer(nil)
113 err = t.Execute(buf, &p)
117 ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1))
125 fset := token.NewFileSet()
126 pkgs, err := parser.ParseDir(fset, *use, nil, 0)
128 log.Fatalf("%q:%v", *use, err)
130 pkg := pkgs["lsp"] // CHECK
132 for fname, f := range files {
133 for _, d := range f.Decls {
134 fd, ok := d.(*ast.FuncDecl)
138 nm := fd.Name.String()
139 if ast.IsExported(nm) {
140 // we're looking for things like didChange
143 if fx, ok := byname[nm]; ok {
145 log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname)
148 // and the Paramnames
150 for _, f := range ft.Params.List {
152 if len(f.Names) > 0 {
153 nm = f.Names[0].String()
155 fx.paramnames = append(fx.paramnames, nm)
161 for i, f := range types {
162 log.Printf("%d %s %s", i, f.Internal, f.Found)
167 type Function struct {
169 Internal string // first letter lower case
174 Result string // do it in code, easier than in a template
176 Found string // file it was found in
179 var types []*Function
180 var byname = map[string]*Function{} // internal names
183 fset := token.NewFileSet()
184 f, err := parser.ParseFile(fset, *def, nil, 0)
188 fd, err := os.Create("/tmp/ast")
192 ast.Fprint(fd, fset, f, ast.NotNilFilter)
193 ast.Inspect(f, inter)
194 sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name })
196 for i, f := range types {
197 log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results)
202 func inter(n ast.Node) bool {
203 x, ok := n.(*ast.TypeSpec)
204 if !ok || x.Name.Name != *typ {
207 m := x.Type.(*ast.InterfaceType).Methods.List
208 for _, fld := range m {
209 fn := fld.Type.(*ast.FuncType)
213 Name: fld.Names[0].String(),
215 fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:]
216 for _, f := range p {
217 fx.paramtypes = append(fx.paramtypes, whatis(f.Type))
219 for _, f := range r {
220 fx.Results = append(fx.Results, whatis(f.Type))
222 types = append(types, fx)
223 byname[fx.Internal] = fx
228 func whatis(x ast.Expr) string {
229 switch n := x.(type) {
230 case *ast.SelectorExpr:
231 return whatis(n.X) + "." + n.Sel.String()
233 return "*" + whatis(n.X)
235 if ast.IsExported(n.Name) {
236 // these are from package protocol
237 return "protocol." + n.Name
241 return "[]" + whatis(n.Elt)
242 case *ast.InterfaceType:
245 log.Fatalf("Fatal %T", x)
246 return fmt.Sprintf("%T", x)