diff --git a/go.mod b/go.mod index e6543b1..6437375 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.12 require ( github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 github.com/arduino/go-paths-helper v1.5.0 + github.com/fatih/color v1.7.0 github.com/pkg/errors v0.9.1 github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 github.com/stretchr/testify v1.6.1 diff --git a/handler/builder.go b/handler/builder.go index e42a4d3..cbdfe17 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -54,6 +54,7 @@ func (handler *InoHandler) rebuildEnvironmentLoop() { // Regenerate preprocessed sketch! done := make(chan bool) go func() { + defer streams.CatchAndLogPanic() handler.progressHandler.Create("arduinoLanguageServerRebuild") handler.progressHandler.Begin("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressBegin{ diff --git a/handler/handler.go b/handler/handler.go index aeea175..dcfd574 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -83,7 +83,10 @@ func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler { handler.clangdStarted = sync.NewCond(&handler.dataMux) stdStream := jsonrpc2.NewBufferedStream(stdio, jsonrpc2.VSCodeObjectCodec{}) var stdHandler jsonrpc2.Handler = jsonrpc2.HandlerWithError(handler.HandleMessageFromIDE) - handler.StdioConn = jsonrpc2.NewConn(context.Background(), stdStream, stdHandler) + handler.StdioConn = jsonrpc2.NewConn(context.Background(), stdStream, stdHandler, + jsonrpc2.OnRecv(streams.JSONRPCConnLogOnRecv("IDE --> LS CL:")), + jsonrpc2.OnSend(streams.JSONRPCConnLogOnSend("IDE <-- LS CL:")), + ) handler.progressHandler = NewProgressProxy(handler.StdioConn) @@ -176,6 +179,8 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr // method "initialize" go func() { + defer streams.CatchAndLogPanic() + // Start clangd asynchronously log.Printf("LS --- initializing workbench (queued)") handler.dataMux.Lock() @@ -514,7 +519,9 @@ func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp. clangdStream := jsonrpc2.NewBufferedStream(clangdStdio, jsonrpc2.VSCodeObjectCodec{}) clangdHandler := AsyncHandler{jsonrpc2.HandlerWithError(handler.FromClangd)} - handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler) + handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler, + jsonrpc2.OnRecv(streams.JSONRPCConnLogOnRecv("IDE LS <-- CL:")), + jsonrpc2.OnSend(streams.JSONRPCConnLogOnSend("IDE LS --> CL:"))) // Send initialization command to clangd ctx, cancel := context.WithTimeout(context.Background(), time.Second) @@ -1087,12 +1094,15 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l if r.DocumentSymbolArray != nil { // Treat the input as []DocumentSymbol + log.Printf(" <-- documentSymbol(%d document symbols)", len(*r.DocumentSymbolArray)) return handler.cpp2inoDocumentSymbols(*r.DocumentSymbolArray, inoURI) } else if r.SymbolInformationArray != nil { // Treat the input as []SymbolInformation + log.Printf(" <-- documentSymbol(%d symbol information)", len(*r.SymbolInformationArray)) return handler.cpp2inoSymbolInformation(*r.SymbolInformationArray) } else { // Treat the input as null + log.Printf(" <-- null documentSymbol") } case *[]lsp.CommandOrCodeAction: diff --git a/streams/jsonrpc2.go b/streams/jsonrpc2.go new file mode 100644 index 0000000..3887399 --- /dev/null +++ b/streams/jsonrpc2.go @@ -0,0 +1,52 @@ +package streams + +import ( + "log" + "runtime/debug" + + "github.com/fatih/color" + "github.com/sourcegraph/jsonrpc2" +) + +var green = color.New(color.FgHiGreen) +var red = color.New(color.FgHiRed) + +// JSONRPCConnLogOnRecv perform logging of the given req and resp +func JSONRPCConnLogOnRecv(prefix string) func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { + return func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { + jsonrpcLog(prefix, req, resp, false) + } +} + +// JSONRPCConnLogOnSend perform logging of the given req and resp +func JSONRPCConnLogOnSend(prefix string) func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { + return func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { + jsonrpcLog(prefix, req, resp, true) + } +} + +func jsonrpcLog(prefix string, req *jsonrpc2.Request, resp *jsonrpc2.Response, sending bool) { + color.NoColor = false + var c *color.Color + if sending { + c = red + } else { + c = green + } + if resp != nil { + if req != nil { + log.Print(c.Sprintf(prefix+" ANSWER %s %v (%v)", req.Method, req.ID, resp.ID)) + } else { + log.Print(c.Sprintf(prefix+" ANSWER UNBOUND (%v)", resp.ID)) + } + } else if req != nil { + if !req.Notif { + log.Print(c.Sprintf(prefix+" REQUEST %s %v", req.Method, req.ID)) + } else { + log.Print(c.Sprintf(prefix+" NOTIFICATION %s", req.Method)) + } + } else { + log.Print(green.Sprintf(prefix + " NULL MESSAGE")) + log.Print(string(debug.Stack())) + } +}