From 9f075f7bfe741c212db778aed6ce67397ec4060a Mon Sep 17 00:00:00 2001 From: Ian Cottrell Date: Thu, 16 Apr 2020 21:12:43 -0400 Subject: [PATCH] internal/lsp: switch the protocol logger to use the new jsonrpc2 message types This saves it from having to know the wire format and understand the decoding tricks. Change-Id: I1f3ef3345ffee736a9d104f8ebfb436404d737c0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/228721 Run-TryBot: Ian Cottrell TryBot-Result: Gobot Gobot Reviewed-by: Robert Findley --- internal/lsp/protocol/log.go | 151 ++++++++++------------------------- 1 file changed, 43 insertions(+), 108 deletions(-) diff --git a/internal/lsp/protocol/log.go b/internal/lsp/protocol/log.go index f7cb076ce1..afc037c954 100644 --- a/internal/lsp/protocol/log.go +++ b/internal/lsp/protocol/log.go @@ -2,7 +2,6 @@ package protocol import ( "context" - "encoding/json" "fmt" "io" "strings" @@ -41,23 +40,6 @@ func (s *loggingStream) Write(ctx context.Context, data []byte) (int64, error) { return count, err } -// wireCombined has all the fields of both Request and Response. -// We can decode this and then work out which it is. -type wireCombined struct { - VersionTag interface{} `json:"jsonrpc"` - ID *jsonrpc2.ID `json:"id,omitempty"` - Method string `json:"method"` - Params *json.RawMessage `json:"params,omitempty"` - Result *json.RawMessage `json:"result,omitempty"` - Error *wireError `json:"error,omitempty"` -} - -type wireError struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data *json.RawMessage `json:"data"` -} - type req struct { method string start time.Time @@ -112,19 +94,18 @@ func (m *mapped) setServer(id string, r req) { const eor = "\r\n\r\n\r\n" -func logCommon(outfd io.Writer, data []byte) (*wireCombined, time.Time, string) { +func logCommon(outfd io.Writer, data []byte) (jsonrpc2.Message, time.Time, string) { if outfd == nil { return nil, time.Time{}, "" } - var v wireCombined - err := json.Unmarshal(data, &v) + v, err := jsonrpc2.DecodeMessage(data) if err != nil { fmt.Fprintf(outfd, "Unmarshal %v\n", err) panic(err) // do better } tm := time.Now() tmfmt := tm.Format("15:04:05.000 PM") - return &v, tm, tmfmt + return v, tm, tmfmt } // logOut and logIn could be combined. "received"<->"Sending", serverCalls<->clientCalls @@ -132,111 +113,65 @@ func logCommon(outfd io.Writer, data []byte) (*wireCombined, time.Time, string) // Writing a message to the client, log it func logOut(outfd io.Writer, data []byte) { - v, tm, tmfmt := logCommon(outfd, data) - if v == nil { - return - } - id := fmt.Sprint(v.ID) - if v.Error != nil { - fmt.Fprintf(outfd, "[Error - %s] Received #%s %s%s", tmfmt, id, v.Error.Message, eor) + msg, tm, tmfmt := logCommon(outfd, data) + if msg == nil { return } + buf := strings.Builder{} fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning - if v.ID != nil && v.Method != "" && v.Params != nil { - fmt.Fprintf(&buf, "Received request '%s - (%s)'.\n", v.Method, id) - fmt.Fprintf(&buf, "Params: %s%s", *v.Params, eor) - maps.setServer(id, req{method: v.Method, start: tm}) - } else if v.ID != nil && v.Method == "" && v.Params == nil { + switch msg := msg.(type) { + case *jsonrpc2.Call: + id := fmt.Sprint(msg.ID()) + fmt.Fprintf(&buf, "Received request '%s - (%s)'.\n", msg.Method(), id) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + maps.setServer(id, req{method: msg.Method(), start: tm}) + case *jsonrpc2.Notification: + fmt.Fprintf(&buf, "Received notification '%s'.\n", msg.Method()) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + case *jsonrpc2.Response: + id := fmt.Sprint(msg.ID()) + if err := msg.Err(); err != nil { + fmt.Fprintf(outfd, "[Error - %s] Received #%s %s%s", tmfmt, id, err, eor) + return + } cc := maps.client(id, true) elapsed := tm.Sub(cc.start) fmt.Fprintf(&buf, "Received response '%s - (%s)' in %dms.\n", cc.method, id, elapsed/time.Millisecond) - if v.Result == nil { - fmt.Fprintf(&buf, "Result: {}%s", eor) - } else { - fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) - } - } else if v.ID == nil && v.Method != "" && v.Params != nil { - p := "null" - if v.Params != nil { - p = string(*v.Params) - } - fmt.Fprintf(&buf, "Received notification '%s'.\n", v.Method) - fmt.Fprintf(&buf, "Params: %s%s", p, eor) - } else { // for completeness, as it should never happen - buf = strings.Builder{} // undo common Trace - fmt.Fprintf(&buf, "[Error - %s] on write ID?%v method:%q Params:%v Result:%v Error:%v%s", - tmfmt, v.ID != nil, v.Method, v.Params != nil, - v.Result != nil, v.Error != nil, eor) - p := "null" - if v.Params != nil { - p = string(*v.Params) - } - r := "null" - if v.Result != nil { - r = string(*v.Result) - } - fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error.Message, eor) + fmt.Fprintf(&buf, "Result: %s%s", msg.Result(), eor) } outfd.Write([]byte(buf.String())) } // Got a message from the client, log it func logIn(outfd io.Writer, data []byte) { - v, tm, tmfmt := logCommon(outfd, data) - if v == nil { - return - } - id := fmt.Sprint(v.ID) - // ID Method Params => Sending request - // ID !Method Result(might be null, but !Params) => Sending response (could we get an Error?) - // !ID Method Params => Sending notification - if v.Error != nil { // does this ever happen? - fmt.Fprintf(outfd, "[Error - %s] Sent #%s %s%s", tmfmt, id, v.Error.Message, eor) + msg, tm, tmfmt := logCommon(outfd, data) + if msg == nil { return } buf := strings.Builder{} fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning - if v.ID != nil && v.Method != "" && (v.Params != nil || v.Method == "shutdown") { - fmt.Fprintf(&buf, "Sending request '%s - (%s)'.\n", v.Method, id) - x := "{}" - if v.Params != nil { - x = string(*v.Params) + switch msg := msg.(type) { + case *jsonrpc2.Call: + id := fmt.Sprint(msg.ID()) + fmt.Fprintf(&buf, "Sending request '%s - (%s)'.\n", msg.Method(), id) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + maps.setServer(id, req{method: msg.Method(), start: tm}) + case *jsonrpc2.Notification: + fmt.Fprintf(&buf, "Sending notification '%s'.\n", msg.Method()) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + case *jsonrpc2.Response: + id := fmt.Sprint(msg.ID()) + if err := msg.Err(); err != nil { + fmt.Fprintf(outfd, "[Error - %s] Sent #%s %s%s", tmfmt, id, err, eor) + return } - fmt.Fprintf(&buf, "Params: %s%s", x, eor) - maps.setClient(id, req{method: v.Method, start: tm}) - } else if v.ID != nil && v.Method == "" && v.Params == nil { - sc := maps.server(id, true) - elapsed := tm.Sub(sc.start) - fmt.Fprintf(&buf, "Sending response '%s - (%s)' took %dms.\n", - sc.method, id, elapsed/time.Millisecond) - if v.Result == nil { - fmt.Fprintf(&buf, "Result: {}%s", eor) - } else { - fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) - } - } else if v.ID == nil && v.Method != "" { - p := "null" - if v.Params != nil { - p = string(*v.Params) - } - fmt.Fprintf(&buf, "Sending notification '%s'.\n", v.Method) - fmt.Fprintf(&buf, "Params: %s%s", p, eor) - } else { // for completeness, as it should never happen - buf = strings.Builder{} // undo common Trace - fmt.Fprintf(&buf, "[Error - %s] on read ID?%v method:%q Params:%v Result:%v Error:%v%s", - tmfmt, v.ID != nil, v.Method, v.Params != nil, - v.Result != nil, v.Error != nil, eor) - p := "null" - if v.Params != nil { - p = string(*v.Params) - } - r := "null" - if v.Result != nil { - r = string(*v.Result) - } - fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error.Message, eor) + cc := maps.client(id, true) + elapsed := tm.Sub(cc.start) + fmt.Fprintf(&buf, "Sending response '%s - (%s)' in %dms.\n", + cc.method, id, elapsed/time.Millisecond) + fmt.Fprintf(&buf, "Result: %s%s", msg.Result(), eor) } outfd.Write([]byte(buf.String())) }