1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:54:45 -07:00

internal/lsp: changing server noun to serve verb

Also adding in --remote support and using it to implement the equivalent
functionality of the external forward command
Also adding in --listen as a replacement for --port as it is more flexible,
specifically it allows localhost:port which
is helpful in environments where opening remotely accesible ports is
problematic.

Change-Id: I5de1cea7dd6f1ee46e7423f3be2a4caca6f040b2
Reviewed-on: https://go-review.googlesource.com/c/161658
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2019-02-07 17:05:20 -05:00
parent 657755b003
commit 44bee7e801
4 changed files with 55 additions and 72 deletions

View File

@ -1,59 +0,0 @@
// The forward command writes and reads to a gopls server on a network socket.
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"golang.org/x/tools/internal/lsp/cmd"
"golang.org/x/tools/internal/tool"
)
func main() {
tool.Main(context.Background(), &app{&cmd.Server{}}, os.Args[1:])
}
type app struct {
*cmd.Server
}
func (*app) Name() string { return "forward" }
func (*app) Usage() string { return "[-port=<value>]" }
func (*app) ShortHelp() string { return "An intermediary between an editor and gopls." }
func (*app) DetailedHelp(*flag.FlagSet) {}
func (a *app) Run(ctx context.Context, args ...string) error {
if a.Server.Port == 0 {
a.ShortHelp()
os.Exit(0)
}
conn, err := net.Dial("tcp", fmt.Sprintf(":%v", a.Server.Port))
if err != nil {
log.Print(err)
os.Exit(0)
}
go func(conn net.Conn) {
_, err := io.Copy(conn, os.Stdin)
if err != nil {
log.Print(err)
os.Exit(0)
}
}(conn)
go func(conn net.Conn) {
_, err := io.Copy(os.Stdout, conn)
if err != nil {
log.Print(err)
os.Exit(0)
}
}(conn)
for {
}
}

View File

@ -25,12 +25,16 @@ type Application struct {
// Embed the basic profiling flags supported by the tool package
tool.Profile
// We include the server directly for now, so the flags work even without the verb.
// TODO: Remove this when we stop allowing the server verb by default.
Server Server
// We include the server configuration directly for now, so the flags work
// even without the verb.
// TODO: Remove this when we stop allowing the serve verb by default.
Serve Serve
// An initial, common go/packages configuration
Config packages.Config
// Support for remote lsp server
Remote string `flag:"remote" help:"*EXPERIMENTAL* - forward all commands to a remote lsp"`
}
// Name implements tool.Application returning the binary name.
@ -65,7 +69,7 @@ gopls flags are:
// temporary measure for compatibility.
func (app *Application) Run(ctx context.Context, args ...string) error {
if len(args) == 0 {
tool.Main(ctx, &app.Server, args)
tool.Main(ctx, &app.Serve, args)
return nil
}
app.Config.Mode = packages.LoadSyntax
@ -87,8 +91,9 @@ func (app *Application) Run(ctx context.Context, args ...string) error {
// command line.
// The command is specified by the first non flag argument.
func (app *Application) commands() []tool.Application {
app.Serve.app = app
return []tool.Application{
&app.Server,
&app.Serve,
&query{app: app},
}
}

View File

@ -15,26 +15,30 @@ import (
"path/filepath"
"strings"
"time"
"net"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp"
"golang.org/x/tools/internal/tool"
)
// Server is a struct that exposes the configurable parts of the LSP server as
// Serve is a struct that exposes the configurable parts of the LSP server as
// flags, in the right form for tool.Main to consume.
type Server struct {
type Serve struct {
Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
Mode string `flag:"mode" help:"no effect"`
Port int `flag:"port" help:"port on which to run gopls for debugging purposes"`
Address string `flag:"listen" help:"address on which to listen for remote connections"`
app *Application
}
func (s *Server) Name() string { return "server" }
func (s *Server) Usage() string { return "" }
func (s *Server) ShortHelp() string {
func (s *Serve) Name() string { return "serve" }
func (s *Serve) Usage() string { return "" }
func (s *Serve) ShortHelp() string {
return "run a server for Go code using the Language Server Protocol"
}
func (s *Server) DetailedHelp(f *flag.FlagSet) {
func (s *Serve) DetailedHelp(f *flag.FlagSet) {
fmt.Fprint(f.Output(), `
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
a child of an editor process.
@ -46,7 +50,7 @@ gopls server flags are:
// Run configures a server based on the flags, and then runs it.
// It blocks until the server shuts down.
func (s *Server) Run(ctx context.Context, args ...string) error {
func (s *Serve) Run(ctx context.Context, args ...string) error {
if len(args) > 0 {
return tool.CommandLineErrorf("server does not take arguments, got %v", args)
}
@ -64,6 +68,9 @@ func (s *Server) Run(ctx context.Context, args ...string) error {
log.SetOutput(io.MultiWriter(os.Stderr, f))
out = f
}
if s.app.Remote != "" {
return s.forward()
}
logger := func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) {
const eol = "\r\n\r\n\r\n"
if err != nil {
@ -112,9 +119,33 @@ func (s *Server) Run(ctx context.Context, args ...string) error {
fmt.Fprintf(out, "%s", outx.String())
}
// For debugging purposes only.
if s.Address != "" {
return lsp.RunServerOnAddress(ctx, s.Address, logger)
}
if s.Port != 0 {
return lsp.RunServerOnPort(ctx, s.Port, logger)
}
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
return lsp.RunServer(ctx, stream, logger)
}
func (s *Serve) forward() error {
conn, err := net.Dial("tcp", s.app.Remote)
if err != nil {
return err
}
errc := make(chan error)
go func(conn net.Conn) {
_, err := io.Copy(conn, os.Stdin)
errc <- err
}(conn)
go func(conn net.Conn) {
_, err := io.Copy(os.Stdout, conn)
errc <- err
}(conn)
return <-errc
}

View File

@ -31,8 +31,14 @@ func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{})
// RunServerOnPort starts an LSP server on the given port and does not exit.
// This function exists for debugging purposes.
func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error {
return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port))
}
// RunServerOnPort starts an LSP server on the given port and does not exit.
// This function exists for debugging purposes.
func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
s := &server{}
ln, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}