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  }