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 / parse / parse.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 // Package parse provides functions to parse LSP logs.
6 // Fully processed logs are returned by ToRLog().
7 package parse
8
9 import (
10         "bufio"
11         "encoding/json"
12         "errors"
13         "fmt"
14         "log"
15         "os"
16         "regexp"
17         "strings"
18 )
19
20 // MsgType is the type of message.
21 type MsgType int
22
23 const (
24         // ClRequest from client to server has method and id
25         ClRequest MsgType = iota
26         // ClResponse from server to client
27         ClResponse
28         // SvRequest from server to client, has method and id
29         SvRequest
30         // SvResponse from client to server
31         SvResponse
32         // ToServer notification has method, but no id
33         ToServer
34         // ToClient notification
35         ToClient
36         // ReportErr is an error message
37         ReportErr // errors have method and id
38 )
39
40 // Logmsg is the type of a parsed log entry.
41 type Logmsg struct {
42         Type    MsgType
43         Method  string
44         ID      string      // for requests/responses. Client and server request ids overlap
45         Elapsed string      // for responses
46         Hdr     string      // header. do we need to keep all these strings?
47         Rest    string      // the unparsed result, with newlines or not
48         Body    interface{} // the parsed result
49 }
50
51 // ReadLogs from a file. Most users should use ToRlog().
52 func ReadLogs(fname string) ([]*Logmsg, error) {
53         byid := make(map[string]int)
54         msgs := []*Logmsg{}
55         fd, err := os.Open(fname)
56         if err != nil {
57                 return nil, err
58         }
59         defer fd.Close()
60         logrdr := bufio.NewScanner(fd)
61         logrdr.Buffer(nil, 1<<25) //  a large buffer, for safety
62         logrdr.Split(scanLogs)
63         for i := 0; logrdr.Scan(); i++ {
64                 flds := strings.SplitN(logrdr.Text(), "\n", 2)
65                 if len(flds) == 1 {
66                         flds = append(flds, "") // for Errors
67                 }
68                 msg, err := parselog(flds[0], flds[1])
69                 if err != nil {
70                         return nil, fmt.Errorf("failed to parse %q: %v", logrdr.Text(), err)
71                 }
72                 switch msg.Type {
73                 case ClRequest, SvRequest:
74                         v, err := msg.unmarshal(Requests(msg.Method))
75                         if err != nil {
76                                 return nil, fmt.Errorf("%v for %s, %T", err, msg.Method, Requests(msg.Method))
77                         }
78                         msg.Body = v
79                 case ClResponse, SvResponse:
80                         v, err := msg.doresponse()
81                         if err != nil {
82                                 return nil, fmt.Errorf("%v %s", err, msg.Method)
83                         }
84                         msg.Body = v
85                 case ToServer, ToClient:
86                         v, err := msg.unmarshal(Notifs(msg.Method))
87                         if err != nil && Notifs(msg.Method) != nil {
88                                 return nil, fmt.Errorf("%s/%T: %v", msg.Method, Notifs(msg.Method), err)
89                         }
90                         msg.Body = v
91                 case ReportErr:
92                         msg.Body = msg.Rest // save cause
93                 }
94                 byid[msg.ID]++
95                 msgs = append(msgs, msg)
96         }
97         if err = logrdr.Err(); err != nil {
98                 return msgs, err
99         }
100         return msgs, nil
101 }
102
103 // parse a single log message, given first line, and the rest
104 func parselog(first, rest string) (*Logmsg, error) {
105         if strings.HasPrefix(rest, "Params: ") {
106                 rest = rest[8:]
107         } else if strings.HasPrefix(rest, "Result: ") {
108                 rest = rest[8:]
109         }
110         msg := &Logmsg{Hdr: first, Rest: rest}
111         fixid := func(s string) string {
112                 // emacs does (n)., gopls does (n)'.
113                 s = strings.Trim(s, "()'.{)")
114                 return s
115         }
116         flds := strings.Fields(first)
117         chk := func(s string, n int) bool { return strings.Contains(first, s) && len(flds) == n }
118         // gopls and emacs differ in how they report elapsed time
119         switch {
120         case chk("Sending request", 9):
121                 msg.Type = ClRequest
122                 msg.Method = flds[6][1:]
123                 msg.ID = fixid(flds[8][:len(flds[8])-2])
124         case chk("Received response", 11):
125                 msg.Type = ClResponse
126                 msg.Method = flds[6][1:]
127                 msg.ID = fixid(flds[8])
128                 msg.Elapsed = flds[10]
129         case chk("Received request", 9):
130                 msg.Type = SvRequest
131                 msg.Method = flds[6][1:]
132                 msg.ID = fixid(flds[8])
133         case chk("Sending response", 11), // gopls
134                 chk("Sending response", 13): // emacs
135                 msg.Type = SvResponse
136                 msg.Method = flds[6][1:]
137                 msg.ID = fixid(flds[8][:len(flds[8])-1])
138                 msg.Elapsed = flds[10]
139         case chk("Sending notification", 7):
140                 msg.Type = ToServer
141                 msg.Method = strings.Trim(flds[6], ".'")
142                 if len(flds) == 9 {
143                         log.Printf("len=%d method=%s %q", len(flds), msg.Method, first)
144                 }
145         case chk("Received notification", 7):
146                 msg.Type = ToClient
147                 msg.Method = flds[6][1 : len(flds[6])-2]
148         case strings.HasPrefix(first, "[Error - "):
149                 msg.Type = ReportErr
150                 both := flds[5]
151                 idx := strings.Index(both, "#") // relies on ID.Number
152                 msg.Method = both[:idx]
153                 msg.ID = fixid(both[idx+1:])
154                 msg.Rest = strings.Join(flds[6:], " ")
155                 msg.Rest = `"` + msg.Rest + `"`
156         default:
157                 return nil, fmt.Errorf("surprise, first=%q with %d flds", first, len(flds))
158         }
159         return msg, nil
160 }
161
162 // unmarshal into a proposed type
163 func (l *Logmsg) unmarshal(p interface{}) (interface{}, error) {
164         r := []byte(l.Rest)
165         if err := json.Unmarshal(r, p); err != nil {
166                 // need general alternatives, but for now
167                 // if p is *[]foo and rest is {}, return an empty p (or *p?)
168                 // or, cheat:
169                 if l.Rest == "{}" {
170                         return nil, nil
171                 }
172                 return nil, err
173         }
174         return p, nil
175 }
176
177 func (l *Logmsg) doresponse() (interface{}, error) {
178         for _, x := range Responses(l.Method) {
179                 v, err := l.unmarshal(x)
180                 if err == nil {
181                         return v, nil
182                 }
183                 if x == nil {
184                         return new(interface{}), nil
185                 }
186         }
187         // failure!
188         rr := Responses(l.Method)
189         for _, x := range rr {
190                 log.Printf("tried %T", x)
191         }
192         log.Fatalf("(%d) doresponse failed for %s %q", len(rr), l.Method, l.Rest)
193         return nil, nil
194 }
195
196 // be a little forgiving in separating log records
197 var recSep = regexp.MustCompile("\n\n\n|\r\n\r\n\r\n")
198
199 // return offset of start of next record, contents of record, error
200 func scanLogs(b []byte, atEOF bool) (int, []byte, error) { //bufio.SplitFunc
201         got := recSep.FindIndex(b)
202         if got == nil {
203                 if atEOF && len(b) > 0 {
204                         return 0, nil, errors.New("malformed log: all logs should end with a separator")
205                 }
206                 return 0, nil, nil
207         }
208         return got[1], b[:got[0]], nil
209 }
210
211 // String returns a user-useful versin of a Direction
212 func (d MsgType) String() string {
213         switch d {
214         case ClRequest:
215                 return "clrequest"
216         case ClResponse:
217                 return "clresponse"
218         case SvRequest:
219                 return "svrequest"
220         case SvResponse:
221                 return "svresponse"
222         case ToServer:
223                 return "toserver"
224         case ToClient:
225                 return "toclient"
226         case ReportErr:
227                 return "reporterr"
228         }
229         return fmt.Sprintf("dirname: %d unknown", d)
230 }