mirror of
https://github.com/golang/go
synced 2024-11-18 11:34: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:
parent
657755b003
commit
44bee7e801
@ -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 {
|
|
||||||
}
|
|
||||||
}
|
|
@ -25,12 +25,16 @@ type Application struct {
|
|||||||
// Embed the basic profiling flags supported by the tool package
|
// Embed the basic profiling flags supported by the tool package
|
||||||
tool.Profile
|
tool.Profile
|
||||||
|
|
||||||
// We include the server directly for now, so the flags work even without the verb.
|
// We include the server configuration directly for now, so the flags work
|
||||||
// TODO: Remove this when we stop allowing the server verb by default.
|
// even without the verb.
|
||||||
Server Server
|
// TODO: Remove this when we stop allowing the serve verb by default.
|
||||||
|
Serve Serve
|
||||||
|
|
||||||
// An initial, common go/packages configuration
|
// An initial, common go/packages configuration
|
||||||
Config packages.Config
|
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.
|
// Name implements tool.Application returning the binary name.
|
||||||
@ -65,7 +69,7 @@ gopls flags are:
|
|||||||
// temporary measure for compatibility.
|
// temporary measure for compatibility.
|
||||||
func (app *Application) Run(ctx context.Context, args ...string) error {
|
func (app *Application) Run(ctx context.Context, args ...string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
tool.Main(ctx, &app.Server, args)
|
tool.Main(ctx, &app.Serve, args)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
app.Config.Mode = packages.LoadSyntax
|
app.Config.Mode = packages.LoadSyntax
|
||||||
@ -87,8 +91,9 @@ func (app *Application) Run(ctx context.Context, args ...string) error {
|
|||||||
// command line.
|
// command line.
|
||||||
// The command is specified by the first non flag argument.
|
// The command is specified by the first non flag argument.
|
||||||
func (app *Application) commands() []tool.Application {
|
func (app *Application) commands() []tool.Application {
|
||||||
|
app.Serve.app = app
|
||||||
return []tool.Application{
|
return []tool.Application{
|
||||||
&app.Server,
|
&app.Serve,
|
||||||
&query{app: app},
|
&query{app: app},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,26 +15,30 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"net"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/jsonrpc2"
|
"golang.org/x/tools/internal/jsonrpc2"
|
||||||
"golang.org/x/tools/internal/lsp"
|
"golang.org/x/tools/internal/lsp"
|
||||||
"golang.org/x/tools/internal/tool"
|
"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.
|
// 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"`
|
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"`
|
Mode string `flag:"mode" help:"no effect"`
|
||||||
Port int `flag:"port" help:"port on which to run gopls for debugging purposes"`
|
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 *Serve) Name() string { return "serve" }
|
||||||
func (s *Server) Usage() string { return "" }
|
func (s *Serve) Usage() string { return "" }
|
||||||
func (s *Server) ShortHelp() string {
|
func (s *Serve) ShortHelp() string {
|
||||||
return "run a server for Go code using the Language Server Protocol"
|
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(), `
|
fmt.Fprint(f.Output(), `
|
||||||
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
|
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
|
||||||
a child of an editor process.
|
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.
|
// Run configures a server based on the flags, and then runs it.
|
||||||
// It blocks until the server shuts down.
|
// 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 {
|
if len(args) > 0 {
|
||||||
return tool.CommandLineErrorf("server does not take arguments, got %v", args)
|
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))
|
log.SetOutput(io.MultiWriter(os.Stderr, f))
|
||||||
out = 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) {
|
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"
|
const eol = "\r\n\r\n\r\n"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -112,9 +119,33 @@ func (s *Server) Run(ctx context.Context, args ...string) error {
|
|||||||
fmt.Fprintf(out, "%s", outx.String())
|
fmt.Fprintf(out, "%s", outx.String())
|
||||||
}
|
}
|
||||||
// For debugging purposes only.
|
// For debugging purposes only.
|
||||||
|
if s.Address != "" {
|
||||||
|
return lsp.RunServerOnAddress(ctx, s.Address, logger)
|
||||||
|
}
|
||||||
if s.Port != 0 {
|
if s.Port != 0 {
|
||||||
return lsp.RunServerOnPort(ctx, s.Port, logger)
|
return lsp.RunServerOnPort(ctx, s.Port, logger)
|
||||||
}
|
}
|
||||||
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
||||||
return lsp.RunServer(ctx, stream, logger)
|
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
|
||||||
|
}
|
@ -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.
|
// RunServerOnPort starts an LSP server on the given port and does not exit.
|
||||||
// This function exists for debugging purposes.
|
// This function exists for debugging purposes.
|
||||||
func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error {
|
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{}
|
s := &server{}
|
||||||
ln, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
|
ln, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user