github.com/v2fly/tools@v0.100.0/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/v2fly/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 } 162 fx.paramnames = append(fx.paramnames, nm) 163 } 164 } 165 } 166 } 167 if false { 168 for i, f := range types { 169 log.Printf("%d %s %s", i, f.Internal, f.Found) 170 } 171 } 172 } 173 174 type Function struct { 175 Name string 176 Internal string // first letter lower case 177 paramtypes []string 178 paramnames []string 179 Results []string 180 Param string 181 Result string // do it in code, easier than in a template 182 Invoke string 183 Found string // file it was found in 184 } 185 186 var types []*Function 187 var byname = map[string]*Function{} // internal names 188 189 func doTypes() { 190 fset := token.NewFileSet() 191 f, err := parser.ParseFile(fset, *def, nil, 0) 192 if err != nil { 193 log.Fatal(err) 194 } 195 fd, err := os.Create("/tmp/ast") 196 if err != nil { 197 log.Fatal(err) 198 } 199 ast.Fprint(fd, fset, f, ast.NotNilFilter) 200 ast.Inspect(f, inter) 201 sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name }) 202 if false { 203 for i, f := range types { 204 log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results) 205 } 206 } 207 } 208 209 func inter(n ast.Node) bool { 210 x, ok := n.(*ast.TypeSpec) 211 if !ok || x.Name.Name != *typ { 212 return true 213 } 214 m := x.Type.(*ast.InterfaceType).Methods.List 215 for _, fld := range m { 216 fn := fld.Type.(*ast.FuncType) 217 p := fn.Params.List 218 r := fn.Results.List 219 fx := &Function{ 220 Name: fld.Names[0].String(), 221 } 222 fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:] 223 for _, f := range p { 224 fx.paramtypes = append(fx.paramtypes, whatis(f.Type)) 225 } 226 for _, f := range r { 227 fx.Results = append(fx.Results, whatis(f.Type)) 228 } 229 types = append(types, fx) 230 byname[fx.Internal] = fx 231 } 232 return false 233 } 234 235 func whatis(x ast.Expr) string { 236 switch n := x.(type) { 237 case *ast.SelectorExpr: 238 return whatis(n.X) + "." + n.Sel.String() 239 case *ast.StarExpr: 240 return "*" + whatis(n.X) 241 case *ast.Ident: 242 if ast.IsExported(n.Name) { 243 // these are from package protocol 244 return "protocol." + n.Name 245 } 246 return n.Name 247 case *ast.ArrayType: 248 return "[]" + whatis(n.Elt) 249 case *ast.InterfaceType: 250 return "interface{}" 251 default: 252 log.Fatalf("Fatal %T", x) 253 return fmt.Sprintf("%T", x) 254 } 255 }