Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.5.2 / integration / replay / main.go
1 // Copyright 2019 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 // Replay logs. See README.md
6 package main
7
8 import (
9         "bufio"
10         "context"
11         "flag"
12         "fmt"
13         "log"
14         "os"
15         "os/exec"
16         "sort"
17         "strconv"
18         "strings"
19
20         "golang.org/x/tools/gopls/integration/parse"
21         "golang.org/x/tools/internal/fakenet"
22         "golang.org/x/tools/internal/jsonrpc2"
23         p "golang.org/x/tools/internal/lsp/protocol"
24 )
25
26 var (
27         command = flag.String("cmd", "", "location of server to send to, looks for gopls")
28         cmp     = flag.Bool("cmp", false, "only compare log and /tmp/seen")
29         logrdr  *bufio.Scanner
30         msgs    []*parse.Logmsg
31         // requests and responses/errors, by id
32         clreq  = make(map[string]*parse.Logmsg)
33         clresp = make(map[string]*parse.Logmsg)
34         svreq  = make(map[string]*parse.Logmsg)
35         svresp = make(map[string]*parse.Logmsg)
36 )
37
38 func main() {
39         log.SetFlags(log.Lshortfile)
40         flag.Usage = func() {
41                 fmt.Fprintln(flag.CommandLine.Output(), "replay [options] <logfile>")
42                 flag.PrintDefaults()
43         }
44         flag.Parse()
45         if flag.NArg() != 1 {
46                 flag.Usage()
47                 os.Exit(2)
48         }
49         logf := flag.Arg(0)
50
51         orig, err := parse.ToRlog(logf)
52         if err != nil {
53                 log.Fatalf("error parsing logfile %q: %v", logf, err)
54         }
55         ctx := context.Background()
56         msgs = orig.Logs
57         log.Printf("old %d, hist:%s", len(msgs), orig.Histogram)
58
59         if !*cmp {
60                 log.Print("calling mimic")
61                 mimic(ctx)
62         }
63         seen, err := parse.ToRlog("/tmp/seen")
64         if err != nil {
65                 log.Fatal(err)
66         }
67         newMsgs := seen.Logs
68         log.Printf("new %d, hist:%s", len(newMsgs), seen.Histogram)
69
70         ok := make(map[string]int)
71         f := func(x []*parse.Logmsg, label string, diags map[p.DocumentURI][]p.Diagnostic) {
72                 counts := make(map[parse.MsgType]int)
73                 for _, l := range x {
74                         if l.Method == "window/logMessage" {
75                                 // don't care
76                                 //continue
77                         }
78                         if l.Method == "textDocument/publishDiagnostics" {
79                                 v, ok := l.Body.(*p.PublishDiagnosticsParams)
80                                 if !ok {
81                                         log.Fatalf("got %T expected PublishDiagnosticsParams", l.Body)
82                                 }
83                                 diags[v.URI] = v.Diagnostics
84                         }
85                         counts[l.Type]++
86                         // notifications only
87                         if l.Type != parse.ToServer && l.Type != parse.ToClient {
88                                 continue
89                         }
90                         s := fmt.Sprintf("%s %s %s", strings.Replace(l.Hdr, "\r", "", -1), label, l.Type)
91                         if i := strings.Index(s, "notification"); i != -1 {
92                                 s = s[i+12:]
93                         }
94                         if len(s) > 120 {
95                                 s = s[:120]
96                         }
97                         ok[s]++
98                 }
99                 msg := ""
100                 for i := parse.ClRequest; i <= parse.ReportErr; i++ {
101                         msg += fmt.Sprintf("%s:%d ", i, counts[i])
102                 }
103                 log.Printf("%s: %s", label, msg)
104         }
105         mdiags := make(map[p.DocumentURI][]p.Diagnostic)
106         f(msgs, "old", mdiags)
107         vdiags := make(map[p.DocumentURI][]p.Diagnostic)
108         f(newMsgs, "new", vdiags)
109         buf := []string{}
110         for k := range ok {
111                 buf = append(buf, fmt.Sprintf("%s %d", k, ok[k]))
112         }
113         if len(buf) > 0 {
114                 log.Printf("counts of notifications")
115                 sort.Strings(buf)
116                 for _, k := range buf {
117                         log.Print(k)
118                 }
119         }
120         buf = buf[0:0]
121         for k, v := range mdiags {
122                 va := vdiags[k]
123                 if len(v) != len(va) {
124                         buf = append(buf, fmt.Sprintf("new has %d, old has %d for %s",
125                                 len(va), len(v), k))
126                 }
127         }
128         for ka := range vdiags {
129                 if _, ok := mdiags[ka]; !ok {
130                         buf = append(buf, fmt.Sprintf("new diagnostics, but no old ones, for %s",
131                                 ka))
132                 }
133         }
134         if len(buf) > 0 {
135                 log.Print("diagnostics differ:")
136                 for _, s := range buf {
137                         log.Print(s)
138                 }
139         }
140 }
141
142 func send(ctx context.Context, l *parse.Logmsg, stream jsonrpc2.Stream, id *jsonrpc2.ID) {
143         if id == nil {
144                 // need to use the number version of ID
145                 n, err := strconv.Atoi(l.ID)
146                 if err != nil {
147                         n = 0
148                 }
149                 nid := jsonrpc2.NewIntID(int64(n))
150                 id = &nid
151         }
152         var msg jsonrpc2.Message
153         var err error
154         switch l.Type {
155         case parse.ClRequest:
156                 msg, err = jsonrpc2.NewCall(*id, l.Method, l.Body)
157         case parse.SvResponse:
158                 msg, err = jsonrpc2.NewResponse(*id, l.Body, nil)
159         case parse.ToServer:
160                 msg, err = jsonrpc2.NewNotification(l.Method, l.Body)
161         default:
162                 log.Fatalf("sending %s", l.Type)
163         }
164         if err != nil {
165                 log.Fatal(err)
166         }
167         stream.Write(ctx, msg)
168 }
169
170 func respond(ctx context.Context, c *jsonrpc2.Call, stream jsonrpc2.Stream) {
171         // c is a server request
172         // pick out the id, and look for the response in msgs
173         id := c.ID()
174         idstr := fmt.Sprint(id)
175         for _, l := range msgs {
176                 if l.ID == idstr && l.Type == parse.SvResponse {
177                         // check that the methods match?
178                         // need to send back the same ID we got.
179                         send(ctx, l, stream, &id)
180                         return
181                 }
182         }
183         log.Fatalf("no response found %q %+v %+v", c.Method(), c.ID(), c)
184 }
185
186 func findgopls() string {
187         totry := [][]string{{"GOBIN", "/gopls"}, {"GOPATH", "/bin/gopls"}, {"HOME", "/go/bin/gopls"}}
188         // looks in the places go install would install:
189         // GOBIN, else GOPATH/bin, else HOME/go/bin
190         ok := func(s string) bool {
191                 fd, err := os.Open(s)
192                 if err != nil {
193                         return false
194                 }
195                 fi, err := fd.Stat()
196                 if err != nil {
197                         return false
198                 }
199                 return fi.Mode()&0111 != 0
200         }
201         for _, t := range totry {
202                 g := os.Getenv(t[0])
203                 if g != "" && ok(g+t[1]) {
204                         gopls := g + t[1]
205                         log.Printf("using gopls at %s", gopls)
206                         return gopls
207                 }
208         }
209         log.Fatal("could not find gopls")
210         return ""
211 }
212
213 func mimic(ctx context.Context) {
214         log.Printf("mimic %d", len(msgs))
215         if *command == "" {
216                 *command = findgopls()
217         }
218         cmd := exec.Command(*command, "-logfile", "/tmp/seen", "-rpc.trace")
219         toServer, err := cmd.StdinPipe()
220         if err != nil {
221                 log.Fatal(err)
222         }
223         fromServer, err := cmd.StdoutPipe()
224         if err != nil {
225                 log.Fatal(err)
226         }
227         err = cmd.Start()
228         if err != nil {
229                 log.Fatal(err)
230         }
231         conn := fakenet.NewConn("stdio", fromServer, toServer)
232         stream := jsonrpc2.NewHeaderStream(conn)
233         rchan := make(chan jsonrpc2.Message, 10) // do we need buffering?
234         rdr := func() {
235                 for {
236                         msg, _, err := stream.Read(ctx)
237                         if err != nil {
238                                 rchan <- nil // close it instead?
239                                 return
240                         }
241                         rchan <- msg
242                 }
243         }
244         go rdr()
245         // send as many as possible: all clrequests and toservers up to a clresponse
246         // and loop
247         seenids := make(map[string]bool) // id's that have been responded to:
248 big:
249         for _, l := range msgs {
250                 switch l.Type {
251                 case parse.ToServer: // just send these as we get to them
252                         send(ctx, l, stream, nil)
253                 case parse.ClRequest:
254                         send(ctx, l, stream, nil) // for now, wait for a response, to make sure code is ok
255                         fallthrough
256                 case parse.ClResponse, parse.ReportErr: // don't go past these until they're received
257                         if seenids[l.ID] {
258                                 break // onward, as it has been received already
259                         }
260                 done:
261                         for {
262                                 msg := <-rchan
263                                 if msg == nil {
264                                         break big
265                                 }
266                                 // if it's svrequest, do something
267                                 // if it's clresponse or reporterr, add to seenids, and if it
268                                 // is l.id, break out of the loop, and continue the outer loop
269
270                                 switch msg := msg.(type) {
271                                 case *jsonrpc2.Call:
272                                         if parse.FromServer(msg.Method()) {
273                                                 respond(ctx, msg, stream)
274                                                 continue done // still waiting
275                                         }
276                                 case *jsonrpc2.Response:
277                                         id := fmt.Sprint(msg.ID())
278                                         seenids[id] = true
279                                         if id == l.ID {
280                                                 break done
281                                         }
282                                 }
283                         }
284                 case parse.SvRequest: // not ours to send
285                         continue
286                 case parse.SvResponse: // sent by us, if the request arrives
287                         continue
288                 case parse.ToClient: // we don't send these
289                         continue
290                 }
291         }
292 }