internal/jsonrpc2: split main loop from construction to fix race
This changes the basic API of a jsonrpc2 connection to run the read loop as a method rather than in a go routine launched in the NewConn. This allows the handler to be created and bound between construction and the read loop starting, which fixes the race. Fixes golang/go#30091 Change-Id: I8201175affe431819cf473e5194d70c019f58425 Reviewed-on: https://go-review.googlesource.com/c/tools/+/170003 Run-TryBot: Ian Cottrell <iancottrell@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
0926561711
commit
a8f40b3f4d
|
@ -19,13 +19,12 @@ import (
|
||||||
// Conn is a JSON RPC 2 client server connection.
|
// Conn is a JSON RPC 2 client server connection.
|
||||||
// Conn is bidirectional; it does not have a designated server or client end.
|
// Conn is bidirectional; it does not have a designated server or client end.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
handle Handler
|
|
||||||
cancel Canceler
|
|
||||||
log Logger
|
|
||||||
stream Stream
|
|
||||||
done chan struct{}
|
|
||||||
err error
|
|
||||||
seq int64 // must only be accessed using atomic operations
|
seq int64 // must only be accessed using atomic operations
|
||||||
|
Handler Handler
|
||||||
|
Canceler Canceler
|
||||||
|
Logger Logger
|
||||||
|
stream Stream
|
||||||
|
err error
|
||||||
pendingMu sync.Mutex // protects the pending map
|
pendingMu sync.Mutex // protects the pending map
|
||||||
pending map[ID]chan *Response
|
pending map[ID]chan *Response
|
||||||
handlingMu sync.Mutex // protects the handling map
|
handlingMu sync.Mutex // protects the handling map
|
||||||
|
@ -57,70 +56,27 @@ func NewErrorf(code int64, format string, args ...interface{}) *Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn creates a new connection object that reads and writes messages from
|
// NewConn creates a new connection object around the supplied stream.
|
||||||
// the supplied stream and dispatches incoming messages to the supplied handler.
|
// You must call Run for the connection to be active.
|
||||||
func NewConn(ctx context.Context, s Stream, options ...interface{}) *Conn {
|
func NewConn(s Stream) *Conn {
|
||||||
conn := &Conn{
|
conn := &Conn{
|
||||||
stream: s,
|
stream: s,
|
||||||
done: make(chan struct{}),
|
|
||||||
pending: make(map[ID]chan *Response),
|
pending: make(map[ID]chan *Response),
|
||||||
handling: make(map[ID]handling),
|
handling: make(map[ID]handling),
|
||||||
}
|
}
|
||||||
for _, opt := range options {
|
|
||||||
switch opt := opt.(type) {
|
|
||||||
case Handler:
|
|
||||||
if conn.handle != nil {
|
|
||||||
panic("Duplicate Handler function in options list")
|
|
||||||
}
|
|
||||||
conn.handle = opt
|
|
||||||
case Canceler:
|
|
||||||
if conn.cancel != nil {
|
|
||||||
panic("Duplicate Canceler function in options list")
|
|
||||||
}
|
|
||||||
conn.cancel = opt
|
|
||||||
case Logger:
|
|
||||||
if conn.log != nil {
|
|
||||||
panic("Duplicate Logger function in options list")
|
|
||||||
}
|
|
||||||
conn.log = opt
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("Unknown option type %T in options list", opt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if conn.handle == nil {
|
|
||||||
// the default handler reports a method error
|
// the default handler reports a method error
|
||||||
conn.handle = func(ctx context.Context, c *Conn, r *Request) {
|
conn.Handler = func(ctx context.Context, c *Conn, r *Request) {
|
||||||
if r.IsNotify() {
|
if r.IsNotify() {
|
||||||
c.Reply(ctx, r, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
|
c.Reply(ctx, r, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if conn.cancel == nil {
|
|
||||||
// the default canceller does nothing
|
// the default canceller does nothing
|
||||||
conn.cancel = func(context.Context, *Conn, *Request) {}
|
conn.Canceler = func(context.Context, *Conn, *Request) {}
|
||||||
}
|
|
||||||
if conn.log == nil {
|
|
||||||
// the default logger does nothing
|
// the default logger does nothing
|
||||||
conn.log = func(Direction, *ID, time.Duration, string, *json.RawMessage, *Error) {}
|
conn.Logger = func(Direction, *ID, time.Duration, string, *json.RawMessage, *Error) {}
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
conn.err = conn.run(ctx)
|
|
||||||
close(conn.done)
|
|
||||||
}()
|
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait blocks until the connection is terminated, and returns any error that
|
|
||||||
// cause the termination.
|
|
||||||
func (c *Conn) Wait(ctx context.Context) error {
|
|
||||||
select {
|
|
||||||
case <-c.done:
|
|
||||||
return c.err
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel cancels a pending Call on the server side.
|
// Cancel cancels a pending Call on the server side.
|
||||||
// The call is identified by its id.
|
// The call is identified by its id.
|
||||||
// JSON RPC 2 does not specify a cancel message, so cancellation support is not
|
// JSON RPC 2 does not specify a cancel message, so cancellation support is not
|
||||||
|
@ -151,7 +107,7 @@ func (c *Conn) Notify(ctx context.Context, method string, params interface{}) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshalling notify request: %v", err)
|
return fmt.Errorf("marshalling notify request: %v", err)
|
||||||
}
|
}
|
||||||
c.log(Send, nil, -1, request.Method, request.Params, nil)
|
c.Logger(Send, nil, -1, request.Method, request.Params, nil)
|
||||||
return c.stream.Write(ctx, data)
|
return c.stream.Write(ctx, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +145,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
||||||
}()
|
}()
|
||||||
// now we are ready to send
|
// now we are ready to send
|
||||||
before := time.Now()
|
before := time.Now()
|
||||||
c.log(Send, request.ID, -1, request.Method, request.Params, nil)
|
c.Logger(Send, request.ID, -1, request.Method, request.Params, nil)
|
||||||
if err := c.stream.Write(ctx, data); err != nil {
|
if err := c.stream.Write(ctx, data); err != nil {
|
||||||
// sending failed, we will never get a response, so don't leave it pending
|
// sending failed, we will never get a response, so don't leave it pending
|
||||||
return err
|
return err
|
||||||
|
@ -198,7 +154,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
||||||
select {
|
select {
|
||||||
case response := <-rchan:
|
case response := <-rchan:
|
||||||
elapsed := time.Since(before)
|
elapsed := time.Since(before)
|
||||||
c.log(Send, response.ID, elapsed, request.Method, response.Result, response.Error)
|
c.Logger(Send, response.ID, elapsed, request.Method, response.Result, response.Error)
|
||||||
// is it an error response?
|
// is it an error response?
|
||||||
if response.Error != nil {
|
if response.Error != nil {
|
||||||
return response.Error
|
return response.Error
|
||||||
|
@ -212,7 +168,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
||||||
return nil
|
return nil
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// allow the handler to propagate the cancel
|
// allow the handler to propagate the cancel
|
||||||
c.cancel(ctx, c, request)
|
c.Canceler(ctx, c, request)
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +211,7 @@ func (c *Conn) Reply(ctx context.Context, req *Request, result interface{}, err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.log(Send, response.ID, elapsed, req.Method, response.Result, response.Error)
|
c.Logger(Send, response.ID, elapsed, req.Method, response.Result, response.Error)
|
||||||
if err = c.stream.Write(ctx, data); err != nil {
|
if err = c.stream.Write(ctx, data); err != nil {
|
||||||
// TODO(iancottrell): if a stream write fails, we really need to shut down
|
// TODO(iancottrell): if a stream write fails, we really need to shut down
|
||||||
// the whole stream
|
// the whole stream
|
||||||
|
@ -281,10 +237,11 @@ type combined struct {
|
||||||
Error *Error `json:"error,omitempty"`
|
Error *Error `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts a read loop on the supplied reader.
|
// Run blocks until the connection is terminated, and returns any error that
|
||||||
|
// caused the termination.
|
||||||
// It must be called exactly once for each Conn.
|
// It must be called exactly once for each Conn.
|
||||||
// It returns only when the reader is closed or there is an error in the stream.
|
// It returns only when the reader is closed or there is an error in the stream.
|
||||||
func (c *Conn) run(ctx context.Context) error {
|
func (c *Conn) Run(ctx context.Context) error {
|
||||||
for {
|
for {
|
||||||
// get the data for a message
|
// get the data for a message
|
||||||
data, err := c.stream.Read(ctx)
|
data, err := c.stream.Read(ctx)
|
||||||
|
@ -297,7 +254,7 @@ func (c *Conn) run(ctx context.Context) error {
|
||||||
if err := json.Unmarshal(data, msg); err != nil {
|
if err := json.Unmarshal(data, msg); err != nil {
|
||||||
// a badly formed message arrived, log it and continue
|
// a badly formed message arrived, log it and continue
|
||||||
// we trust the stream to have isolated the error to just this message
|
// we trust the stream to have isolated the error to just this message
|
||||||
c.log(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err))
|
c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// work out which kind of message we have
|
// work out which kind of message we have
|
||||||
|
@ -310,9 +267,9 @@ func (c *Conn) run(ctx context.Context) error {
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
}
|
}
|
||||||
if request.IsNotify() {
|
if request.IsNotify() {
|
||||||
c.log(Receive, request.ID, -1, request.Method, request.Params, nil)
|
c.Logger(Receive, request.ID, -1, request.Method, request.Params, nil)
|
||||||
// we have a Notify, forward to the handler in a go routine
|
// we have a Notify, forward to the handler in a go routine
|
||||||
c.handle(ctx, c, request)
|
c.Handler(ctx, c, request)
|
||||||
} else {
|
} else {
|
||||||
// we have a Call, forward to the handler in another go routine
|
// we have a Call, forward to the handler in another go routine
|
||||||
reqCtx, cancelReq := context.WithCancel(ctx)
|
reqCtx, cancelReq := context.WithCancel(ctx)
|
||||||
|
@ -323,8 +280,8 @@ func (c *Conn) run(ctx context.Context) error {
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
}
|
}
|
||||||
c.handlingMu.Unlock()
|
c.handlingMu.Unlock()
|
||||||
c.log(Receive, request.ID, -1, request.Method, request.Params, nil)
|
c.Logger(Receive, request.ID, -1, request.Method, request.Params, nil)
|
||||||
c.handle(reqCtx, c, request)
|
c.Handler(reqCtx, c, request)
|
||||||
}
|
}
|
||||||
case msg.ID != nil:
|
case msg.ID != nil:
|
||||||
// we have a response, get the pending entry from the map
|
// we have a response, get the pending entry from the map
|
||||||
|
@ -343,7 +300,7 @@ func (c *Conn) run(ctx context.Context) error {
|
||||||
rchan <- response
|
rchan <- response
|
||||||
close(rchan)
|
close(rchan)
|
||||||
default:
|
default:
|
||||||
c.log(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring"))
|
c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,42 +90,36 @@ func TestHeaderCall(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*testHandler, *testHandler) {
|
func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*jsonrpc2.Conn, *jsonrpc2.Conn) {
|
||||||
a := &testHandler{t: t}
|
aR, bW := io.Pipe()
|
||||||
b := &testHandler{t: t}
|
bR, aW := io.Pipe()
|
||||||
a.reader, b.writer = io.Pipe()
|
a := run(ctx, t, withHeaders, aR, aW)
|
||||||
b.reader, a.writer = io.Pipe()
|
b := run(ctx, t, withHeaders, bR, bW)
|
||||||
for _, h := range []*testHandler{a, b} {
|
|
||||||
h := h
|
|
||||||
if withHeaders {
|
|
||||||
h.stream = jsonrpc2.NewHeaderStream(h.reader, h.writer)
|
|
||||||
} else {
|
|
||||||
h.stream = jsonrpc2.NewStream(h.reader, h.writer)
|
|
||||||
}
|
|
||||||
args := []interface{}{jsonrpc2.Handler(handle)}
|
|
||||||
if *logRPC {
|
|
||||||
args = append(args, jsonrpc2.Log)
|
|
||||||
}
|
|
||||||
h.Conn = jsonrpc2.NewConn(ctx, h.stream, args...)
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
h.reader.Close()
|
|
||||||
h.writer.Close()
|
|
||||||
}()
|
|
||||||
if err := h.Conn.Wait(ctx); err != nil {
|
|
||||||
t.Fatalf("Stream failed: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
return a, b
|
return a, b
|
||||||
}
|
}
|
||||||
|
|
||||||
type testHandler struct {
|
func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w io.WriteCloser) *jsonrpc2.Conn {
|
||||||
t *testing.T
|
var stream jsonrpc2.Stream
|
||||||
reader *io.PipeReader
|
if withHeaders {
|
||||||
writer *io.PipeWriter
|
stream = jsonrpc2.NewHeaderStream(r, w)
|
||||||
stream jsonrpc2.Stream
|
} else {
|
||||||
*jsonrpc2.Conn
|
stream = jsonrpc2.NewStream(r, w)
|
||||||
|
}
|
||||||
|
conn := jsonrpc2.NewConn(stream)
|
||||||
|
conn.Handler = handle
|
||||||
|
if *logRPC {
|
||||||
|
conn.Logger = jsonrpc2.Log
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
r.Close()
|
||||||
|
w.Close()
|
||||||
|
}()
|
||||||
|
if err := conn.Run(ctx); err != nil {
|
||||||
|
t.Fatalf("Stream failed: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) {
|
func handle(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) {
|
||||||
|
|
|
@ -121,22 +121,26 @@ func (app *Application) connect(ctx context.Context, client protocol.Client) (pr
|
||||||
var server protocol.Server
|
var server protocol.Server
|
||||||
switch app.Remote {
|
switch app.Remote {
|
||||||
case "":
|
case "":
|
||||||
server = lsp.NewServer(client)
|
server = lsp.NewClientServer(client)
|
||||||
case "internal":
|
case "internal":
|
||||||
cr, sw, _ := os.Pipe()
|
cr, sw, _ := os.Pipe()
|
||||||
sr, cw, _ := os.Pipe()
|
sr, cw, _ := os.Pipe()
|
||||||
_, server = protocol.RunClient(ctx, jsonrpc2.NewHeaderStream(cr, cw), client)
|
var jc *jsonrpc2.Conn
|
||||||
go lsp.RunServer(ctx, jsonrpc2.NewHeaderStream(sr, sw))
|
jc, server = protocol.NewClient(jsonrpc2.NewHeaderStream(cr, cw), client)
|
||||||
|
go jc.Run(ctx)
|
||||||
|
go lsp.NewServer(jsonrpc2.NewHeaderStream(sr, sw)).Run(ctx)
|
||||||
default:
|
default:
|
||||||
conn, err := net.Dial("tcp", app.Remote)
|
conn, err := net.Dial("tcp", app.Remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
||||||
_, server = protocol.RunClient(ctx, stream, client)
|
var jc *jsonrpc2.Conn
|
||||||
|
jc, server = protocol.NewClient(stream, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
go jc.Run(ctx)
|
||||||
}
|
}
|
||||||
params := &protocol.InitializeParams{}
|
params := &protocol.InitializeParams{}
|
||||||
params.RootURI = string(span.FileURI(app.Config.Dir))
|
params.RootURI = string(span.FileURI(app.Config.Dir))
|
||||||
|
|
|
@ -119,14 +119,20 @@ func (s *Serve) 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.
|
||||||
|
run := func(srv *lsp.Server) {
|
||||||
|
srv.Conn.Logger = logger
|
||||||
|
go srv.Conn.Run(ctx)
|
||||||
|
}
|
||||||
if s.Address != "" {
|
if s.Address != "" {
|
||||||
return lsp.RunServerOnAddress(ctx, s.Address, logger)
|
return lsp.RunServerOnAddress(ctx, s.Address, run)
|
||||||
}
|
}
|
||||||
if s.Port != 0 {
|
if s.Port != 0 {
|
||||||
return lsp.RunServerOnPort(ctx, s.Port, logger)
|
return lsp.RunServerOnPort(ctx, s.Port, run)
|
||||||
}
|
}
|
||||||
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
||||||
return lsp.RunServer(ctx, stream, logger)
|
srv := lsp.NewServer(stream)
|
||||||
|
srv.Conn.Logger = logger
|
||||||
|
return srv.Conn.Run(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Serve) forward() error {
|
func (s *Serve) forward() error {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"golang.org/x/tools/internal/span"
|
"golang.org/x/tools/internal/span"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
|
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
|
||||||
if err := s.setContent(ctx, uri, []byte(content)); err != nil {
|
if err := s.setContent(ctx, uri, []byte(content)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func (s *server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) setContent(ctx context.Context, uri span.URI, content []byte) error {
|
func (s *Server) setContent(ctx context.Context, uri span.URI, content []byte) error {
|
||||||
return s.view.SetContent(ctx, uri, content)
|
return s.view.SetContent(ctx, uri, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
|
return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &server{
|
s := &Server{
|
||||||
view: cache.NewView("lsp_test", span.FileURI(cfg.Dir), &cfg),
|
view: cache.NewView("lsp_test", span.FileURI(cfg.Dir), &cfg),
|
||||||
}
|
}
|
||||||
// Do a first pass to collect special markers for completion.
|
// Do a first pass to collect special markers for completion.
|
||||||
|
@ -285,7 +285,7 @@ Failed:
|
||||||
return msg.String()
|
return msg.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c completions) test(t *testing.T, exported *packagestest.Exported, s *server, items completionItems) {
|
func (c completions) test(t *testing.T, exported *packagestest.Exported, s *Server, items completionItems) {
|
||||||
for src, itemList := range c {
|
for src, itemList := range c {
|
||||||
var want []protocol.CompletionItem
|
var want []protocol.CompletionItem
|
||||||
for _, pos := range itemList {
|
for _, pos := range itemList {
|
||||||
|
@ -406,7 +406,7 @@ Failed:
|
||||||
return msg.String()
|
return msg.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f formats) test(t *testing.T, s *server) {
|
func (f formats) test(t *testing.T, s *Server) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for filename, gofmted := range f {
|
for filename, gofmted := range f {
|
||||||
uri := span.FileURI(filename)
|
uri := span.FileURI(filename)
|
||||||
|
@ -444,7 +444,7 @@ func (f formats) collect(pos token.Position) {
|
||||||
f[pos.Filename] = stdout.String()
|
f[pos.Filename] = stdout.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d definitions) test(t *testing.T, s *server, typ bool) {
|
func (d definitions) test(t *testing.T, s *Server, typ bool) {
|
||||||
for src, target := range d {
|
for src, target := range d {
|
||||||
params := &protocol.TextDocumentPositionParams{
|
params := &protocol.TextDocumentPositionParams{
|
||||||
TextDocument: protocol.TextDocumentIdentifier{
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
|
@ -495,7 +495,7 @@ func (h highlights) collect(e *packagestest.Exported, fset *token.FileSet, name
|
||||||
h[name] = append(h[name], loc)
|
h[name] = append(h[name], loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h highlights) test(t *testing.T, s *server) {
|
func (h highlights) test(t *testing.T, s *Server) {
|
||||||
for name, locations := range h {
|
for name, locations := range h {
|
||||||
params := &protocol.TextDocumentPositionParams{
|
params := &protocol.TextDocumentPositionParams{
|
||||||
TextDocument: protocol.TextDocumentIdentifier{
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
|
@ -547,7 +547,7 @@ func (s symbols) collect(e *packagestest.Exported, fset *token.FileSet, name str
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s symbols) test(t *testing.T, server *server) {
|
func (s symbols) test(t *testing.T, server *Server) {
|
||||||
for uri, expectedSymbols := range s {
|
for uri, expectedSymbols := range s {
|
||||||
params := &protocol.DocumentSymbolParams{
|
params := &protocol.DocumentSymbolParams{
|
||||||
TextDocument: protocol.TextDocumentIdentifier{
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
|
|
|
@ -15,15 +15,17 @@ func canceller(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request)
|
||||||
conn.Notify(context.Background(), "$/cancelRequest", &CancelParams{ID: *req.ID})
|
conn.Notify(context.Background(), "$/cancelRequest", &CancelParams{ID: *req.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunClient(ctx context.Context, stream jsonrpc2.Stream, client Client, opts ...interface{}) (*jsonrpc2.Conn, Server) {
|
func NewClient(stream jsonrpc2.Stream, client Client) (*jsonrpc2.Conn, Server) {
|
||||||
opts = append([]interface{}{clientHandler(client), jsonrpc2.Canceler(canceller)}, opts...)
|
conn := jsonrpc2.NewConn(stream)
|
||||||
conn := jsonrpc2.NewConn(ctx, stream, opts...)
|
conn.Handler = clientHandler(client)
|
||||||
|
conn.Canceler = jsonrpc2.Canceler(canceller)
|
||||||
return conn, &serverDispatcher{Conn: conn}
|
return conn, &serverDispatcher{Conn: conn}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunServer(ctx context.Context, stream jsonrpc2.Stream, server Server, opts ...interface{}) (*jsonrpc2.Conn, Client) {
|
func NewServer(stream jsonrpc2.Stream, server Server) (*jsonrpc2.Conn, Client) {
|
||||||
opts = append([]interface{}{serverHandler(server), jsonrpc2.Canceler(canceller)}, opts...)
|
conn := jsonrpc2.NewConn(stream)
|
||||||
conn := jsonrpc2.NewConn(ctx, stream, opts...)
|
conn.Handler = serverHandler(server)
|
||||||
|
conn.Canceler = jsonrpc2.Canceler(canceller)
|
||||||
return conn, &clientDispatcher{Conn: conn}
|
return conn, &clientDispatcher{Conn: conn}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,32 +25,33 @@ import (
|
||||||
"golang.org/x/tools/internal/span"
|
"golang.org/x/tools/internal/span"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewServer
|
// NewClientServer
|
||||||
func NewServer(client protocol.Client) protocol.Server {
|
func NewClientServer(client protocol.Client) *Server {
|
||||||
return &server{
|
return &Server{
|
||||||
client: client,
|
client: client,
|
||||||
configured: make(chan struct{}),
|
configured: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunServer starts an LSP server on the supplied stream, and waits until the
|
// NewServer starts an LSP server on the supplied stream, and waits until the
|
||||||
// stream is closed.
|
// stream is closed.
|
||||||
func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{}) error {
|
func NewServer(stream jsonrpc2.Stream) *Server {
|
||||||
s := NewServer(nil).(*server)
|
s := &Server{
|
||||||
conn, client := protocol.RunServer(ctx, stream, s, opts...)
|
configured: make(chan struct{}),
|
||||||
s.client = client
|
}
|
||||||
return conn.Wait(ctx)
|
s.Conn, s.client = protocol.NewServer(stream, s)
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, h func(s *Server)) error {
|
||||||
return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port))
|
return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port), h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
|
func RunServerOnAddress(ctx context.Context, addr string, h func(s *Server)) error {
|
||||||
ln, err := net.Listen("tcp", addr)
|
ln, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -61,11 +62,14 @@ func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
||||||
go RunServer(ctx, stream, opts...)
|
s := NewServer(stream)
|
||||||
|
h(s)
|
||||||
|
go s.Run(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type server struct {
|
type Server struct {
|
||||||
|
Conn *jsonrpc2.Conn
|
||||||
client protocol.Client
|
client protocol.Client
|
||||||
|
|
||||||
initializedMu sync.Mutex
|
initializedMu sync.Mutex
|
||||||
|
@ -81,7 +85,11 @@ type server struct {
|
||||||
configured chan struct{}
|
configured chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
|
func (s *Server) Run(ctx context.Context) error {
|
||||||
|
return s.Conn.Run(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
|
||||||
s.initializedMu.Lock()
|
s.initializedMu.Lock()
|
||||||
defer s.initializedMu.Unlock()
|
defer s.initializedMu.Unlock()
|
||||||
if s.initialized {
|
if s.initialized {
|
||||||
|
@ -151,7 +159,7 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
func (s *Server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
||||||
go func() {
|
go func() {
|
||||||
// we hae to do this in a go routine to unblock the jsonrpc processor
|
// we hae to do this in a go routine to unblock the jsonrpc processor
|
||||||
// but we also have to block all calls to packages.Load until this is done
|
// but we also have to block all calls to packages.Load until this is done
|
||||||
|
@ -179,7 +187,7 @@ func (s *server) Initialized(ctx context.Context, params *protocol.InitializedPa
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Shutdown(context.Context) error {
|
func (s *Server) Shutdown(context.Context) error {
|
||||||
s.initializedMu.Lock()
|
s.initializedMu.Lock()
|
||||||
defer s.initializedMu.Unlock()
|
defer s.initializedMu.Unlock()
|
||||||
if !s.initialized {
|
if !s.initialized {
|
||||||
|
@ -189,7 +197,7 @@ func (s *server) Shutdown(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Exit(ctx context.Context) error {
|
func (s *Server) Exit(ctx context.Context) error {
|
||||||
if s.initialized {
|
if s.initialized {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -197,31 +205,31 @@ func (s *server) Exit(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidChangeWorkspaceFolders(context.Context, *protocol.DidChangeWorkspaceFoldersParams) error {
|
func (s *Server) DidChangeWorkspaceFolders(context.Context, *protocol.DidChangeWorkspaceFoldersParams) error {
|
||||||
return notImplemented("DidChangeWorkspaceFolders")
|
return notImplemented("DidChangeWorkspaceFolders")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidChangeConfiguration(context.Context, *protocol.DidChangeConfigurationParams) error {
|
func (s *Server) DidChangeConfiguration(context.Context, *protocol.DidChangeConfigurationParams) error {
|
||||||
return notImplemented("DidChangeConfiguration")
|
return notImplemented("DidChangeConfiguration")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidChangeWatchedFiles(context.Context, *protocol.DidChangeWatchedFilesParams) error {
|
func (s *Server) DidChangeWatchedFiles(context.Context, *protocol.DidChangeWatchedFilesParams) error {
|
||||||
return notImplemented("DidChangeWatchedFiles")
|
return notImplemented("DidChangeWatchedFiles")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Symbols(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
|
func (s *Server) Symbols(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
|
||||||
return nil, notImplemented("Symbols")
|
return nil, notImplemented("Symbols")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) ExecuteCommand(context.Context, *protocol.ExecuteCommandParams) (interface{}, error) {
|
func (s *Server) ExecuteCommand(context.Context, *protocol.ExecuteCommandParams) (interface{}, error) {
|
||||||
return nil, notImplemented("ExecuteCommand")
|
return nil, notImplemented("ExecuteCommand")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
|
func (s *Server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
|
||||||
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), params.TextDocument.Text)
|
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), params.TextDocument.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {
|
func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {
|
||||||
if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil {
|
if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil {
|
||||||
// If range is empty, we expect the full content of file, i.e. a single change with no range.
|
// If range is empty, we expect the full content of file, i.e. a single change with no range.
|
||||||
change := params.ContentChanges[0]
|
change := params.ContentChanges[0]
|
||||||
|
@ -257,7 +265,7 @@ func (s *server) applyChanges(ctx context.Context, params *protocol.DidChangeTex
|
||||||
return string(content), nil
|
return string(content), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
|
func (s *Server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
|
||||||
if len(params.ContentChanges) < 1 {
|
if len(params.ContentChanges) < 1 {
|
||||||
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
|
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
|
||||||
}
|
}
|
||||||
|
@ -281,24 +289,24 @@ func (s *server) DidChange(ctx context.Context, params *protocol.DidChangeTextDo
|
||||||
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), text)
|
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
|
func (s *Server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
|
||||||
return notImplemented("WillSave")
|
return notImplemented("WillSave")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
|
func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
|
||||||
return nil, notImplemented("WillSaveWaitUntil")
|
return nil, notImplemented("WillSaveWaitUntil")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidSave(context.Context, *protocol.DidSaveTextDocumentParams) error {
|
func (s *Server) DidSave(context.Context, *protocol.DidSaveTextDocumentParams) error {
|
||||||
return nil // ignore
|
return nil // ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
|
func (s *Server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
|
||||||
s.setContent(ctx, span.NewURI(params.TextDocument.URI), nil)
|
s.setContent(ctx, span.NewURI(params.TextDocument.URI), nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
func (s *Server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -321,11 +329,11 @@ func (s *server) Completion(ctx context.Context, params *protocol.CompletionPara
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) CompletionResolve(context.Context, *protocol.CompletionItem) (*protocol.CompletionItem, error) {
|
func (s *Server) CompletionResolve(context.Context, *protocol.CompletionItem) (*protocol.CompletionItem, error) {
|
||||||
return nil, notImplemented("CompletionResolve")
|
return nil, notImplemented("CompletionResolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
|
func (s *Server) Hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -364,7 +372,7 @@ func (s *server) Hover(ctx context.Context, params *protocol.TextDocumentPositio
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
|
func (s *Server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -384,7 +392,7 @@ func (s *server) SignatureHelp(ctx context.Context, params *protocol.TextDocumen
|
||||||
return toProtocolSignatureHelp(info), nil
|
return toProtocolSignatureHelp(info), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
func (s *Server) Definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -416,7 +424,7 @@ func (s *server) Definition(ctx context.Context, params *protocol.TextDocumentPo
|
||||||
return []protocol.Location{loc}, nil
|
return []protocol.Location{loc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) TypeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
func (s *Server) TypeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -448,15 +456,15 @@ func (s *server) TypeDefinition(ctx context.Context, params *protocol.TextDocume
|
||||||
return []protocol.Location{loc}, nil
|
return []protocol.Location{loc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Implementation(context.Context, *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
func (s *Server) Implementation(context.Context, *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
|
||||||
return nil, notImplemented("Implementation")
|
return nil, notImplemented("Implementation")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) References(context.Context, *protocol.ReferenceParams) ([]protocol.Location, error) {
|
func (s *Server) References(context.Context, *protocol.ReferenceParams) ([]protocol.Location, error) {
|
||||||
return nil, notImplemented("References")
|
return nil, notImplemented("References")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DocumentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
|
func (s *Server) DocumentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -476,7 +484,7 @@ func (s *server) DocumentHighlight(ctx context.Context, params *protocol.TextDoc
|
||||||
return toProtocolHighlight(m, spans), nil
|
return toProtocolHighlight(m, spans), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
|
func (s *Server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
|
||||||
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -485,7 +493,7 @@ func (s *server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSy
|
||||||
return toProtocolDocumentSymbols(m, symbols), nil
|
return toProtocolDocumentSymbols(m, symbols), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
func (s *Server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
|
||||||
_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -511,36 +519,36 @@ func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionPara
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
|
func (s *Server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
|
||||||
return nil, nil // ignore
|
return nil, nil // ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) CodeLensResolve(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
|
func (s *Server) CodeLensResolve(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
|
||||||
return nil, notImplemented("CodeLensResolve")
|
return nil, notImplemented("CodeLensResolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DocumentLink(context.Context, *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
|
func (s *Server) DocumentLink(context.Context, *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
|
||||||
return nil, nil // ignore
|
return nil, nil // ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DocumentLinkResolve(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
|
func (s *Server) DocumentLinkResolve(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
|
||||||
return nil, notImplemented("DocumentLinkResolve")
|
return nil, notImplemented("DocumentLinkResolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
|
func (s *Server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
|
||||||
return nil, notImplemented("DocumentColor")
|
return nil, notImplemented("DocumentColor")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
|
func (s *Server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
|
||||||
return nil, notImplemented("ColorPresentation")
|
return nil, notImplemented("ColorPresentation")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
|
func (s *Server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
|
||||||
spn := span.New(span.URI(params.TextDocument.URI), span.Point{}, span.Point{})
|
spn := span.New(span.URI(params.TextDocument.URI), span.Point{}, span.Point{})
|
||||||
return formatRange(ctx, s.view, spn)
|
return formatRange(ctx, s.view, spn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
|
func (s *Server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
|
||||||
_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -552,26 +560,26 @@ func (s *server) RangeFormatting(ctx context.Context, params *protocol.DocumentR
|
||||||
return formatRange(ctx, s.view, spn)
|
return formatRange(ctx, s.view, spn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
|
func (s *Server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
|
||||||
return nil, notImplemented("OnTypeFormatting")
|
return nil, notImplemented("OnTypeFormatting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Rename(context.Context, *protocol.RenameParams) ([]protocol.WorkspaceEdit, error) {
|
func (s *Server) Rename(context.Context, *protocol.RenameParams) ([]protocol.WorkspaceEdit, error) {
|
||||||
return nil, notImplemented("Rename")
|
return nil, notImplemented("Rename")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) FoldingRanges(context.Context, *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
|
func (s *Server) FoldingRanges(context.Context, *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
|
||||||
return nil, notImplemented("FoldingRanges")
|
return nil, notImplemented("FoldingRanges")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Error(err error) {
|
func (s *Server) Error(err error) {
|
||||||
s.client.LogMessage(context.Background(), &protocol.LogMessageParams{
|
s.client.LogMessage(context.Background(), &protocol.LogMessageParams{
|
||||||
Type: protocol.Error,
|
Type: protocol.Error,
|
||||||
Message: fmt.Sprint(err),
|
Message: fmt.Sprint(err),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) processConfig(config interface{}) error {
|
func (s *Server) processConfig(config interface{}) error {
|
||||||
//TODO: we should probably store and process more of the config
|
//TODO: we should probably store and process more of the config
|
||||||
c, ok := config.(map[string]interface{})
|
c, ok := config.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
Loading…
Reference in New Issue