github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/helper/helper.go (about) 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. 4 5 // Invoke with //go:generate helper/helper -t Server -d protocol/tsserver.go -u lsp -o server_gen.go 6 // invoke in internal/lsp 7 package main 8 9 import ( 10 "bytes" 11 "flag" 12 "fmt" 13 "go/ast" 14 "go/format" 15 "go/parser" 16 "go/token" 17 "log" 18 "os" 19 "sort" 20 "strings" 21 "text/template" 22 ) 23 24 var ( 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") 29 ) 30 31 func main() { 32 log.SetFlags(log.Lshortfile) 33 flag.Parse() 34 if *typ == "" || *def == "" || *use == "" || *out == "" { 35 flag.PrintDefaults() 36 return 37 } 38 // read the type definition and see what methods we're looking for 39 doTypes() 40 41 // parse the package and see which methods are defined 42 doUses() 43 44 output() 45 } 46 47 // replace "\\\n" with nothing before using 48 var tmpl = `// Copyright 2021 The Go Authors. All rights reserved. 49 // Use of this source code is governed by a BSD-style 50 // license that can be found in the LICENSE file. 51 52 package lsp 53 54 // code generated by helper. DO NOT EDIT. 55 56 import ( 57 "context" 58 59 "github.com/powerman/golang-tools/internal/lsp/protocol" 60 ) 61 62 {{range $key, $v := .Stuff}} 63 func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} { 64 {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\ 65 {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}} 66 } 67 {{end}} 68 ` 69 70 func output() { 71 // put in empty param names as needed 72 for _, t := range types { 73 if t.paramnames == nil { 74 t.paramnames = make([]string, len(t.paramtypes)) 75 } 76 for i, p := range t.paramtypes { 77 cm := "" 78 if i > 0 { 79 cm = ", " 80 } 81 t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p) 82 this := t.paramnames[i] 83 if this == "_" { 84 this = "nil" 85 } 86 t.Invoke += fmt.Sprintf("%s%s", cm, this) 87 } 88 if len(t.Results) > 1 { 89 t.Result = "(" 90 } 91 for i, r := range t.Results { 92 cm := "" 93 if i > 0 { 94 cm = ", " 95 } 96 t.Result += fmt.Sprintf("%s%s", cm, r) 97 } 98 if len(t.Results) > 1 { 99 t.Result += ")" 100 } 101 } 102 103 fd, err := os.Create(*out) 104 if err != nil { 105 log.Fatal(err) 106 } 107 t, err := template.New("foo").Parse(tmpl) 108 if err != nil { 109 log.Fatal(err) 110 } 111 type par struct { 112 Type string 113 Stuff []*Function 114 } 115 p := par{*typ, types} 116 if false { // debugging the template 117 t.Execute(os.Stderr, &p) 118 } 119 buf := bytes.NewBuffer(nil) 120 err = t.Execute(buf, &p) 121 if err != nil { 122 log.Fatal(err) 123 } 124 ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1)) 125 if err != nil { 126 log.Fatal(err) 127 } 128 fd.Write(ans) 129 } 130 131 func doUses() { 132 fset := token.NewFileSet() 133 pkgs, err := parser.ParseDir(fset, *use, nil, 0) 134 if err != nil { 135 log.Fatalf("%q:%v", *use, err) 136 } 137 pkg := pkgs["lsp"] // CHECK 138 files := pkg.Files 139 for fname, f := range files { 140 for _, d := range f.Decls { 141 fd, ok := d.(*ast.FuncDecl) 142 if !ok { 143 continue 144 } 145 nm := fd.Name.String() 146 if ast.IsExported(nm) { 147 // we're looking for things like didChange 148 continue 149 } 150 if fx, ok := byname[nm]; ok { 151 if fx.Found != "" { 152 log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname) 153 } 154 fx.Found = fname 155 // and the Paramnames 156 ft := fd.Type 157 for _, f := range ft.Params.List { 158 nm := "" 159 if len(f.Names) > 0 { 160 nm = f.Names[0].String() 161 if nm == "_" { 162 nm = "_gen" 163 } 164 } 165 fx.paramnames = append(fx.paramnames, nm) 166 } 167 } 168 } 169 } 170 if false { 171 for i, f := range types { 172 log.Printf("%d %s %s", i, f.Internal, f.Found) 173 } 174 } 175 } 176 177 type Function struct { 178 Name string 179 Internal string // first letter lower case 180 paramtypes []string 181 paramnames []string 182 Results []string 183 Param string 184 Result string // do it in code, easier than in a template 185 Invoke string 186 Found string // file it was found in 187 } 188 189 var types []*Function 190 var byname = map[string]*Function{} // internal names 191 192 func doTypes() { 193 fset := token.NewFileSet() 194 f, err := parser.ParseFile(fset, *def, nil, 0) 195 if err != nil { 196 log.Fatal(err) 197 } 198 fd, err := os.Create("/tmp/ast") 199 if err != nil { 200 log.Fatal(err) 201 } 202 ast.Fprint(fd, fset, f, ast.NotNilFilter) 203 ast.Inspect(f, inter) 204 sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name }) 205 if false { 206 for i, f := range types { 207 log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results) 208 } 209 } 210 } 211 212 func inter(n ast.Node) bool { 213 x, ok := n.(*ast.TypeSpec) 214 if !ok || x.Name.Name != *typ { 215 return true 216 } 217 m := x.Type.(*ast.InterfaceType).Methods.List 218 for _, fld := range m { 219 fn := fld.Type.(*ast.FuncType) 220 p := fn.Params.List 221 r := fn.Results.List 222 fx := &Function{ 223 Name: fld.Names[0].String(), 224 } 225 fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:] 226 for _, f := range p { 227 fx.paramtypes = append(fx.paramtypes, whatis(f.Type)) 228 } 229 for _, f := range r { 230 fx.Results = append(fx.Results, whatis(f.Type)) 231 } 232 types = append(types, fx) 233 byname[fx.Internal] = fx 234 } 235 return false 236 } 237 238 func whatis(x ast.Expr) string { 239 switch n := x.(type) { 240 case *ast.SelectorExpr: 241 return whatis(n.X) + "." + n.Sel.String() 242 case *ast.StarExpr: 243 return "*" + whatis(n.X) 244 case *ast.Ident: 245 if ast.IsExported(n.Name) { 246 // these are from package protocol 247 return "protocol." + n.Name 248 } 249 return n.Name 250 case *ast.ArrayType: 251 return "[]" + whatis(n.Elt) 252 case *ast.InterfaceType: 253 return "interface{}" 254 default: 255 log.Fatalf("Fatal %T", x) 256 return fmt.Sprintf("%T", x) 257 } 258 }