blob: ce619ae18c5576e992ab1270af65b96be826e485 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The golsp command is an LSP server for Go.
// The Language Server Protocol allows any text editor
// to be extended with IDE-like features;
// see https://langserver.org/ for details.
package main // import "golang.org/x/tools/cmd/golsp"
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/pprof"
"runtime/trace"
"time"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp"
)
var (
cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
memprofile = flag.String("memprofile", "", "write memory profile to this file")
traceFlag = flag.String("trace", "", "write trace log to this file")
// Flags for compatitibility with VSCode.
logfile = flag.String("logfile", "", "filename to log to")
mode = flag.String("mode", "", "no effect")
)
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: golsp [flags]\n")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() > 0 {
flag.Usage()
os.Exit(2)
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer pprof.StopCPUProfile()
}
if *traceFlag != "" {
f, err := os.Create(*traceFlag)
if err != nil {
log.Fatal(err)
}
if err := trace.Start(f); err != nil {
log.Fatal(err)
}
// NB: trace log won't be written in case of error.
defer func() {
trace.Stop()
log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag)
}()
}
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
// NB: memprofile won't be written in case of error.
defer func() {
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalf("Writing memory profile: %v", err)
}
f.Close()
}()
}
out := os.Stderr
if *logfile != "" {
f, err := os.Create(*logfile)
if err != nil {
log.Fatalf("Unable to create log file: %v", err)
}
defer f.Close()
log.SetOutput(io.MultiWriter(os.Stderr, f))
out = f
}
if err := lsp.RunServer(
context.Background(),
jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout),
func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) {
if err != nil {
fmt.Fprintf(out, "[Error - %v] %s %s%s %v", time.Now().Format("3:04:05 PM"), direction, method, id, err)
return
}
fmt.Fprintf(out, "[Trace - %v] ", time.Now().Format("3:04:05 PM"))
switch direction {
case jsonrpc2.Send:
fmt.Fprint(out, "Received ")
case jsonrpc2.Receive:
fmt.Fprint(out, "Sending ")
}
switch {
case id == nil:
fmt.Fprint(out, "notification ")
case elapsed >= 0:
fmt.Fprint(out, "response ")
default:
fmt.Fprint(out, "request ")
}
fmt.Fprintf(out, "'%s", method)
switch {
case id == nil:
// do nothing
case id.Name != "":
fmt.Fprintf(out, " - (%s)", id.Name)
default:
fmt.Fprintf(out, " - (%d)", id.Number)
}
fmt.Fprint(out, "'")
if elapsed >= 0 {
fmt.Fprintf(out, " in %vms", elapsed.Nanoseconds()/1000)
}
params := string(*payload)
if params == "null" {
params = "{}"
}
fmt.Fprintf(out, ".\r\nParams: %s\r\n\r\n\r\n", params)
},
); err != nil {
log.Fatal(err)
}
}