Changed: DB Params
This commit is contained in:
29
templ/lsp/LICENSE
Normal file
29
templ/lsp/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2019, The Go Language Server Authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
3
templ/lsp/README.md
Normal file
3
templ/lsp/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# lsp
|
||||
|
||||
Forked from https://github.com/go-language-server repos.
|
86
templ/lsp/jsonrpc2/codes.go
Normal file
86
templ/lsp/jsonrpc2/codes.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
// Code is an error code as defined in the JSON-RPC spec.
|
||||
type Code int32
|
||||
|
||||
// list of JSON-RPC error codes.
|
||||
const (
|
||||
// ParseError is the invalid JSON was received by the server.
|
||||
// An error occurred on the server while parsing the JSON text.
|
||||
ParseError Code = -32700
|
||||
|
||||
// InvalidRequest is the JSON sent is not a valid Request object.
|
||||
InvalidRequest Code = -32600
|
||||
|
||||
// MethodNotFound is the method does not exist / is not available.
|
||||
MethodNotFound Code = -32601
|
||||
|
||||
// InvalidParams is the invalid method parameter(s).
|
||||
InvalidParams Code = -32602
|
||||
|
||||
// InternalError is the internal JSON-RPC error.
|
||||
InternalError Code = -32603
|
||||
|
||||
// JSONRPCReservedErrorRangeStart is the start range of JSON RPC reserved error codes.
|
||||
//
|
||||
// It doesn't denote a real error code. No LSP error codes should
|
||||
// be defined between the start and end range. For backwards
|
||||
// compatibility the "ServerNotInitialized" and the "UnknownErrorCode"
|
||||
// are left in the range.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
JSONRPCReservedErrorRangeStart Code = -32099
|
||||
|
||||
// CodeServerErrorStart reserved for implementation-defined server-errors.
|
||||
//
|
||||
// Deprecated: Use JSONRPCReservedErrorRangeStart instead.
|
||||
CodeServerErrorStart = JSONRPCReservedErrorRangeStart
|
||||
|
||||
// ServerNotInitialized is the error of server not initialized.
|
||||
ServerNotInitialized Code = -32002
|
||||
|
||||
// UnknownError should be used for all non coded errors.
|
||||
UnknownError Code = -32001
|
||||
|
||||
// JSONRPCReservedErrorRangeEnd is the start range of JSON RPC reserved error codes.
|
||||
//
|
||||
// It doesn't denote a real error code.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
JSONRPCReservedErrorRangeEnd Code = -32000
|
||||
|
||||
// CodeServerErrorEnd reserved for implementation-defined server-errors.
|
||||
//
|
||||
// Deprecated: Use JSONRPCReservedErrorRangeEnd instead.
|
||||
CodeServerErrorEnd = JSONRPCReservedErrorRangeEnd
|
||||
)
|
||||
|
||||
// This file contains the Go forms of the wire specification.
|
||||
//
|
||||
// See http://www.jsonrpc.org/specification for details.
|
||||
//
|
||||
// list of JSON-RPC errors.
|
||||
var (
|
||||
// ErrUnknown should be used for all non coded errors.
|
||||
ErrUnknown = NewError(UnknownError, "JSON-RPC unknown error")
|
||||
|
||||
// ErrParse is used when invalid JSON was received by the server.
|
||||
ErrParse = NewError(ParseError, "JSON-RPC parse error")
|
||||
|
||||
// ErrInvalidRequest is used when the JSON sent is not a valid Request object.
|
||||
ErrInvalidRequest = NewError(InvalidRequest, "JSON-RPC invalid request")
|
||||
|
||||
// ErrMethodNotFound should be returned by the handler when the method does
|
||||
// not exist / is not available.
|
||||
ErrMethodNotFound = NewError(MethodNotFound, "JSON-RPC method not found")
|
||||
|
||||
// ErrInvalidParams should be returned by the handler when method
|
||||
// parameter(s) were invalid.
|
||||
ErrInvalidParams = NewError(InvalidParams, "JSON-RPC invalid params")
|
||||
|
||||
// ErrInternal is not currently returned but defined for completeness.
|
||||
ErrInternal = NewError(InternalError, "JSON-RPC internal error")
|
||||
)
|
244
templ/lsp/jsonrpc2/conn.go
Normal file
244
templ/lsp/jsonrpc2/conn.go
Normal file
@@ -0,0 +1,244 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Conn is the common interface to jsonrpc clients and servers.
|
||||
//
|
||||
// Conn is bidirectional; it does not have a designated server or client end.
|
||||
// It manages the jsonrpc2 protocol, connecting responses back to their calls.
|
||||
type Conn interface {
|
||||
// Call invokes the target method and waits for a response.
|
||||
//
|
||||
// The params will be marshaled to JSON before sending over the wire, and will
|
||||
// be handed to the method invoked.
|
||||
//
|
||||
// The response will be unmarshaled from JSON into the result.
|
||||
//
|
||||
// The id returned will be unique from this connection, and can be used for
|
||||
// logging or tracking.
|
||||
Call(ctx context.Context, method string, params, result any) (ID, error)
|
||||
|
||||
// Notify invokes the target method but does not wait for a response.
|
||||
//
|
||||
// The params will be marshaled to JSON before sending over the wire, and will
|
||||
// be handed to the method invoked.
|
||||
Notify(ctx context.Context, method string, params any) error
|
||||
|
||||
// Go starts a goroutine to handle the connection.
|
||||
//
|
||||
// It must be called exactly once for each Conn. It returns immediately.
|
||||
// Must block on Done() to wait for the connection to shut down.
|
||||
//
|
||||
// This is a temporary measure, this should be started automatically in the
|
||||
// future.
|
||||
Go(ctx context.Context, handler Handler)
|
||||
|
||||
// Close closes the connection and it's underlying stream.
|
||||
//
|
||||
// It does not wait for the close to complete, use the Done() channel for
|
||||
// that.
|
||||
Close() error
|
||||
|
||||
// Done returns a channel that will be closed when the processing goroutine
|
||||
// has terminated, which will happen if Close() is called or an underlying
|
||||
// stream is closed.
|
||||
Done() <-chan struct{}
|
||||
|
||||
// Err returns an error if there was one from within the processing goroutine.
|
||||
//
|
||||
// If err returns non nil, the connection will be already closed or closing.
|
||||
Err() error
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
seq int32 // access atomically
|
||||
writeMu sync.Mutex // protects writes to the stream
|
||||
stream Stream // supplied stream
|
||||
pendingMu sync.Mutex // protects the pending map
|
||||
pending map[ID]chan *Response // holds the pending response channel with the ID as the key.
|
||||
|
||||
done chan struct{} // closed when done
|
||||
err atomic.Value // holds run error
|
||||
}
|
||||
|
||||
// NewConn creates a new connection object around the supplied stream.
|
||||
func NewConn(s Stream) Conn {
|
||||
conn := &conn{
|
||||
stream: s,
|
||||
pending: make(map[ID]chan *Response),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
// Call implements Conn.
|
||||
func (c *conn) Call(ctx context.Context, method string, params, result any) (id ID, err error) {
|
||||
// generate a new request identifier
|
||||
id = NewNumberID(atomic.AddInt32(&c.seq, 1))
|
||||
call, err := NewCall(id, method, params)
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("marshaling call parameters: %w", err)
|
||||
}
|
||||
|
||||
// We have to add ourselves to the pending map before we send, otherwise we
|
||||
// are racing the response. Also add a buffer to rchan, so that if we get a
|
||||
// wire response between the time this call is cancelled and id is deleted
|
||||
// from c.pending, the send to rchan will not block.
|
||||
rchan := make(chan *Response, 1)
|
||||
|
||||
c.pendingMu.Lock()
|
||||
c.pending[id] = rchan
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
defer func() {
|
||||
c.pendingMu.Lock()
|
||||
delete(c.pending, id)
|
||||
c.pendingMu.Unlock()
|
||||
}()
|
||||
|
||||
// now we are ready to send
|
||||
_, err = c.write(ctx, call)
|
||||
if err != nil {
|
||||
// sending failed, we will never get a response, so don't leave it pending
|
||||
return id, err
|
||||
}
|
||||
|
||||
// now wait for the response
|
||||
select {
|
||||
case resp := <-rchan:
|
||||
// is it an error response?
|
||||
if resp.err != nil {
|
||||
return id, resp.err
|
||||
}
|
||||
|
||||
if result == nil || len(resp.result) == 0 {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(resp.result))
|
||||
if err := dec.Decode(result); err != nil {
|
||||
return id, fmt.Errorf("unmarshaling result: %w", err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
|
||||
case <-ctx.Done():
|
||||
return id, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Notify implements Conn.
|
||||
func (c *conn) Notify(ctx context.Context, method string, params any) (err error) {
|
||||
notify, err := NewNotification(method, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling notify parameters: %w", err)
|
||||
}
|
||||
|
||||
_, err = c.write(ctx, notify)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *conn) replier(req Message) Replier {
|
||||
return func(ctx context.Context, result any, err error) error {
|
||||
call, ok := req.(*Call)
|
||||
if !ok {
|
||||
// request was a notify, no need to respond
|
||||
return nil
|
||||
}
|
||||
|
||||
response, err := NewResponse(call.id, result, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.write(ctx, response)
|
||||
if err != nil {
|
||||
// TODO(iancottrell): if a stream write fails, we really need to shut down the whole stream
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
|
||||
c.writeMu.Lock()
|
||||
n, err := c.stream.Write(ctx, msg)
|
||||
c.writeMu.Unlock()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("write to stream: %w", err)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Go implements Conn.
|
||||
func (c *conn) Go(ctx context.Context, handler Handler) {
|
||||
go c.run(ctx, handler)
|
||||
}
|
||||
|
||||
func (c *conn) run(ctx context.Context, handler Handler) {
|
||||
defer close(c.done)
|
||||
|
||||
for {
|
||||
// get the next message
|
||||
msg, _, err := c.stream.Read(ctx)
|
||||
if err != nil {
|
||||
// The stream failed, we cannot continue.
|
||||
c.fail(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case Request:
|
||||
if err := handler(ctx, c.replier(msg), msg); err != nil {
|
||||
c.fail(err)
|
||||
}
|
||||
|
||||
case *Response:
|
||||
// If method is not set, this should be a response, in which case we must
|
||||
// have an id to send the response back to the caller.
|
||||
c.pendingMu.Lock()
|
||||
rchan, ok := c.pending[msg.id]
|
||||
c.pendingMu.Unlock()
|
||||
if ok {
|
||||
rchan <- msg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements Conn.
|
||||
func (c *conn) Close() error {
|
||||
return c.stream.Close()
|
||||
}
|
||||
|
||||
// Done implements Conn.
|
||||
func (c *conn) Done() <-chan struct{} {
|
||||
return c.done
|
||||
}
|
||||
|
||||
// Err implements Conn.
|
||||
func (c *conn) Err() error {
|
||||
if err := c.err.Load(); err != nil {
|
||||
return err.(error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fail sets a failure condition on the stream and closes it.
|
||||
func (c *conn) fail(err error) {
|
||||
c.err.Store(err)
|
||||
c.stream.Close()
|
||||
}
|
70
templ/lsp/jsonrpc2/errors.go
Normal file
70
templ/lsp/jsonrpc2/errors.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Error represents a JSON-RPC error.
|
||||
type Error struct {
|
||||
// Code a number indicating the error type that occurred.
|
||||
Code Code `json:"code"`
|
||||
|
||||
// Message a string providing a short description of the error.
|
||||
Message string `json:"message"`
|
||||
|
||||
// Data a Primitive or Structured value that contains additional
|
||||
// information about the error. Can be omitted.
|
||||
Data *json.RawMessage `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// compile time check whether the Error implements error interface.
|
||||
var _ error = (*Error)(nil)
|
||||
|
||||
// Error implements error.Error.
|
||||
func (e *Error) Error() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// Unwrap implements errors.Unwrap.
|
||||
//
|
||||
// Returns the error underlying the receiver, which may be nil.
|
||||
func (e *Error) Unwrap() error { return errors.New(e.Message) }
|
||||
|
||||
// NewError builds a Error struct for the suppied code and message.
|
||||
func NewError(c Code, message string) *Error {
|
||||
return &Error{
|
||||
Code: c,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf builds a Error struct for the suppied code, format and args.
|
||||
func Errorf(c Code, format string, args ...any) *Error {
|
||||
return &Error{
|
||||
Code: c,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// constErr represents a error constant.
|
||||
type constErr string
|
||||
|
||||
// compile time check whether the constErr implements error interface.
|
||||
var _ error = (*constErr)(nil)
|
||||
|
||||
// Error implements error.Error.
|
||||
func (e constErr) Error() string { return string(e) }
|
||||
|
||||
const (
|
||||
// ErrIdleTimeout is returned when serving timed out waiting for new connections.
|
||||
ErrIdleTimeout = constErr("timed out waiting for new connections")
|
||||
)
|
120
templ/lsp/jsonrpc2/handler.go
Normal file
120
templ/lsp/jsonrpc2/handler.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Handler is invoked to handle incoming requests.
|
||||
//
|
||||
// The Replier sends a reply to the request and must be called exactly once.
|
||||
type Handler func(ctx context.Context, reply Replier, req Request) error
|
||||
|
||||
// Replier is passed to handlers to allow them to reply to the request.
|
||||
//
|
||||
// If err is set then result will be ignored.
|
||||
type Replier func(ctx context.Context, result any, err error) error
|
||||
|
||||
// MethodNotFoundHandler is a Handler that replies to all call requests with the
|
||||
// standard method not found response.
|
||||
//
|
||||
// This should normally be the final handler in a chain.
|
||||
func MethodNotFoundHandler(ctx context.Context, reply Replier, req Request) error {
|
||||
return reply(ctx, nil, fmt.Errorf("%q: %w", req.Method(), ErrMethodNotFound))
|
||||
}
|
||||
|
||||
// ReplyHandler creates a Handler that panics if the wrapped handler does
|
||||
// not call Reply for every request that it is passed.
|
||||
func ReplyHandler(handler Handler) (h Handler) {
|
||||
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
|
||||
called := false
|
||||
err := handler(ctx, func(ctx context.Context, result any, err error) error {
|
||||
if called {
|
||||
panic(fmt.Errorf("request %q replied to more than once", req.Method()))
|
||||
}
|
||||
called = true
|
||||
|
||||
return reply(ctx, result, err)
|
||||
}, req)
|
||||
if !called {
|
||||
panic(fmt.Errorf("request %q was never replied to", req.Method()))
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// CancelHandler returns a handler that supports cancellation, and a function
|
||||
// that can be used to trigger canceling in progress requests.
|
||||
func CancelHandler(handler Handler) (h Handler, canceller func(id ID)) {
|
||||
var mu sync.Mutex
|
||||
handling := make(map[ID]context.CancelFunc)
|
||||
|
||||
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
|
||||
if call, ok := req.(*Call); ok {
|
||||
cancelCtx, cancel := context.WithCancel(ctx)
|
||||
ctx = cancelCtx
|
||||
|
||||
mu.Lock()
|
||||
handling[call.ID()] = cancel
|
||||
mu.Unlock()
|
||||
|
||||
innerReply := reply
|
||||
reply = func(ctx context.Context, result any, err error) error {
|
||||
mu.Lock()
|
||||
delete(handling, call.ID())
|
||||
mu.Unlock()
|
||||
return innerReply(ctx, result, err)
|
||||
}
|
||||
}
|
||||
return handler(ctx, reply, req)
|
||||
})
|
||||
|
||||
canceller = func(id ID) {
|
||||
mu.Lock()
|
||||
cancel, found := handling[id]
|
||||
mu.Unlock()
|
||||
if found {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
return h, canceller
|
||||
}
|
||||
|
||||
// AsyncHandler returns a handler that processes each request goes in its own
|
||||
// goroutine.
|
||||
//
|
||||
// The handler returns immediately, without the request being processed.
|
||||
// Each request then waits for the previous request to finish before it starts.
|
||||
//
|
||||
// This allows the stream to unblock at the cost of unbounded goroutines
|
||||
// all stalled on the previous one.
|
||||
func AsyncHandler(handler Handler) (h Handler) {
|
||||
nextRequest := make(chan struct{})
|
||||
close(nextRequest)
|
||||
|
||||
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
|
||||
waitForPrevious := nextRequest
|
||||
nextRequest = make(chan struct{})
|
||||
unlockNext := nextRequest
|
||||
innerReply := reply
|
||||
reply = func(ctx context.Context, result any, err error) error {
|
||||
close(unlockNext)
|
||||
return innerReply(ctx, result, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-waitForPrevious
|
||||
_ = handler(ctx, reply, req)
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
|
||||
return h
|
||||
}
|
7
templ/lsp/jsonrpc2/jsonrpc2.go
Normal file
7
templ/lsp/jsonrpc2/jsonrpc2.go
Normal file
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package jsonrpc2 is an implementation of the JSON-RPC 2 specification for Go.
|
||||
//
|
||||
// https://www.jsonrpc.org/specification
|
||||
package jsonrpc2 // import "github.com/a-h/templ/lsp/jsonrpc2"
|
171
templ/lsp/jsonrpc2/jsonrpc2_test.go
Normal file
171
templ/lsp/jsonrpc2/jsonrpc2_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
)
|
||||
|
||||
const (
|
||||
methodNoArgs = "no_args"
|
||||
methodOneString = "one_string"
|
||||
methodOneNumber = "one_number"
|
||||
methodJoin = "join"
|
||||
)
|
||||
|
||||
type callTest struct {
|
||||
method string
|
||||
params any
|
||||
expect any
|
||||
}
|
||||
|
||||
var callTests = []callTest{
|
||||
{
|
||||
method: methodNoArgs,
|
||||
params: nil,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
method: methodOneString,
|
||||
params: "fish",
|
||||
expect: "got:fish",
|
||||
},
|
||||
{
|
||||
method: methodOneNumber,
|
||||
params: 10,
|
||||
expect: "got:10",
|
||||
},
|
||||
{
|
||||
method: methodJoin,
|
||||
params: []string{"a", "b", "c"},
|
||||
expect: "a/b/c",
|
||||
},
|
||||
// TODO: expand the test cases
|
||||
}
|
||||
|
||||
func (test *callTest) newResults() any {
|
||||
switch e := test.expect.(type) {
|
||||
case []any:
|
||||
var r []any
|
||||
for _, v := range e {
|
||||
r = append(r, reflect.New(reflect.TypeOf(v)).Interface())
|
||||
}
|
||||
return r
|
||||
|
||||
case nil:
|
||||
return nil
|
||||
|
||||
default:
|
||||
return reflect.New(reflect.TypeOf(test.expect)).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func (test *callTest) verifyResults(t *testing.T, results any) {
|
||||
t.Helper()
|
||||
|
||||
if results == nil {
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(results)).Interface()
|
||||
if !reflect.DeepEqual(val, test.expect) {
|
||||
t.Errorf("%v:Results are incorrect, got %+v expect %+v", test.method, val, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequest(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
a, b, done := prepare(ctx, t)
|
||||
defer done()
|
||||
|
||||
for _, test := range callTests {
|
||||
t.Run(test.method, func(t *testing.T) {
|
||||
results := test.newResults()
|
||||
if _, err := a.Call(ctx, test.method, test.params, results); err != nil {
|
||||
t.Fatalf("%v:Call failed: %v", test.method, err)
|
||||
}
|
||||
test.verifyResults(t, results)
|
||||
|
||||
if _, err := b.Call(ctx, test.method, test.params, results); err != nil {
|
||||
t.Fatalf("%v:Call failed: %v", test.method, err)
|
||||
}
|
||||
test.verifyResults(t, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepare(ctx context.Context, t *testing.T) (a, b jsonrpc2.Conn, done func()) {
|
||||
t.Helper()
|
||||
|
||||
// make a wait group that can be used to wait for the system to shut down
|
||||
aPipe, bPipe := net.Pipe()
|
||||
a = run(ctx, aPipe)
|
||||
b = run(ctx, bPipe)
|
||||
done = func() {
|
||||
a.Close()
|
||||
b.Close()
|
||||
<-a.Done()
|
||||
<-b.Done()
|
||||
}
|
||||
|
||||
return a, b, done
|
||||
}
|
||||
|
||||
func run(ctx context.Context, nc io.ReadWriteCloser) jsonrpc2.Conn {
|
||||
stream := jsonrpc2.NewStream(nc)
|
||||
conn := jsonrpc2.NewConn(stream)
|
||||
conn.Go(ctx, testHandler())
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func testHandler() jsonrpc2.Handler {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
switch req.Method() {
|
||||
case methodNoArgs:
|
||||
if len(req.Params()) > 0 {
|
||||
return reply(ctx, nil, fmt.Errorf("expected no params: %w", jsonrpc2.ErrInvalidParams))
|
||||
}
|
||||
return reply(ctx, true, nil)
|
||||
|
||||
case methodOneString:
|
||||
var v string
|
||||
dec := json.NewDecoder(bytes.NewReader(req.Params()))
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, "got:"+v, nil)
|
||||
|
||||
case methodOneNumber:
|
||||
var v int
|
||||
dec := json.NewDecoder(bytes.NewReader(req.Params()))
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, fmt.Sprintf("got:%d", v), nil)
|
||||
|
||||
case methodJoin:
|
||||
var v []string
|
||||
dec := json.NewDecoder(bytes.NewReader(req.Params()))
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, path.Join(v...), nil)
|
||||
|
||||
default:
|
||||
return jsonrpc2.MethodNotFoundHandler(ctx, reply, req)
|
||||
}
|
||||
}
|
||||
}
|
354
templ/lsp/jsonrpc2/message.go
Normal file
354
templ/lsp/jsonrpc2/message.go
Normal file
@@ -0,0 +1,354 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Message is the interface to all JSON-RPC message types.
|
||||
//
|
||||
// They share no common functionality, but are a closed set of concrete types
|
||||
// that are allowed to implement this interface.
|
||||
//
|
||||
// The message types are *Call, *Response and *Notification.
|
||||
type Message interface {
|
||||
// jsonrpc2Message is used to make the set of message implementations a
|
||||
// closed set.
|
||||
jsonrpc2Message()
|
||||
}
|
||||
|
||||
// Request is the shared interface to jsonrpc2 messages that request
|
||||
// a method be invoked.
|
||||
//
|
||||
// The request types are a closed set of *Call and *Notification.
|
||||
type Request interface {
|
||||
Message
|
||||
|
||||
// Method is a string containing the method name to invoke.
|
||||
Method() string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
Params() json.RawMessage
|
||||
|
||||
// jsonrpc2Request is used to make the set of request implementations closed.
|
||||
jsonrpc2Request()
|
||||
}
|
||||
|
||||
// Call is a request that expects a response.
|
||||
//
|
||||
// The response will have a matching ID.
|
||||
type Call struct {
|
||||
// Method is a string containing the method name to invoke.
|
||||
method string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
params json.RawMessage
|
||||
// id of this request, used to tie the Response back to the request.
|
||||
id ID
|
||||
}
|
||||
|
||||
// make sure a Call implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
|
||||
var (
|
||||
_ Request = (*Call)(nil)
|
||||
_ json.Marshaler = (*Call)(nil)
|
||||
_ json.Unmarshaler = (*Call)(nil)
|
||||
)
|
||||
|
||||
// NewCall constructs a new Call message for the supplied ID, method and
|
||||
// parameters.
|
||||
func NewCall(id ID, method string, params any) (*Call, error) {
|
||||
p, merr := marshalInterface(params)
|
||||
req := &Call{
|
||||
id: id,
|
||||
method: method,
|
||||
params: p,
|
||||
}
|
||||
return req, merr
|
||||
}
|
||||
|
||||
// ID returns the current call id.
|
||||
func (c *Call) ID() ID { return c.id }
|
||||
|
||||
// Method implements Request.
|
||||
func (c *Call) Method() string { return c.method }
|
||||
|
||||
// Params implements Request.
|
||||
func (c *Call) Params() json.RawMessage { return c.params }
|
||||
|
||||
// jsonrpc2Message implements Request.
|
||||
func (Call) jsonrpc2Message() {}
|
||||
|
||||
// jsonrpc2Request implements Request.
|
||||
func (Call) jsonrpc2Request() {}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (c Call) MarshalJSON() ([]byte, error) {
|
||||
req := wireRequest{
|
||||
Method: c.method,
|
||||
Params: &c.params,
|
||||
ID: &c.id,
|
||||
}
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling call: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (c *Call) UnmarshalJSON(data []byte) error {
|
||||
var req wireRequest
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
if err := dec.Decode(&req); err != nil {
|
||||
return fmt.Errorf("unmarshaling call: %w", err)
|
||||
}
|
||||
|
||||
c.method = req.Method
|
||||
if req.Params != nil {
|
||||
c.params = *req.Params
|
||||
}
|
||||
if req.ID != nil {
|
||||
c.id = *req.ID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Response is a reply to a Request.
|
||||
//
|
||||
// It will have the same ID as the call it is a response to.
|
||||
type Response struct {
|
||||
// result is the content of the response.
|
||||
result json.RawMessage
|
||||
// err is set only if the call failed.
|
||||
err error
|
||||
// ID of the request this is a response to.
|
||||
id ID
|
||||
}
|
||||
|
||||
// make sure a Response implements the Message, json.Marshaler and json.Unmarshaler and interfaces.
|
||||
var (
|
||||
_ Message = (*Response)(nil)
|
||||
_ json.Marshaler = (*Response)(nil)
|
||||
_ json.Unmarshaler = (*Response)(nil)
|
||||
)
|
||||
|
||||
// NewResponse constructs a new Response message that is a reply to the
|
||||
// supplied. If err is set result may be ignored.
|
||||
func NewResponse(id ID, result any, err error) (*Response, error) {
|
||||
r, merr := marshalInterface(result)
|
||||
resp := &Response{
|
||||
id: id,
|
||||
result: r,
|
||||
err: err,
|
||||
}
|
||||
return resp, merr
|
||||
}
|
||||
|
||||
// ID returns the current response id.
|
||||
func (r *Response) ID() ID { return r.id }
|
||||
|
||||
// Result returns the Response result.
|
||||
func (r *Response) Result() json.RawMessage { return r.result }
|
||||
|
||||
// Err returns the Response error.
|
||||
func (r *Response) Err() error { return r.err }
|
||||
|
||||
// jsonrpc2Message implements Message.
|
||||
func (r *Response) jsonrpc2Message() {}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (r Response) MarshalJSON() ([]byte, error) {
|
||||
resp := &wireResponse{
|
||||
Error: toError(r.err),
|
||||
ID: &r.id,
|
||||
}
|
||||
if resp.Error == nil {
|
||||
resp.Result = &r.result
|
||||
}
|
||||
|
||||
data, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling notification: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (r *Response) UnmarshalJSON(data []byte) error {
|
||||
var resp wireResponse
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
if err := dec.Decode(&resp); err != nil {
|
||||
return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
|
||||
}
|
||||
|
||||
if resp.Result != nil {
|
||||
r.result = *resp.Result
|
||||
}
|
||||
if resp.Error != nil {
|
||||
r.err = resp.Error
|
||||
}
|
||||
if resp.ID != nil {
|
||||
r.id = *resp.ID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toError(err error) *Error {
|
||||
if err == nil {
|
||||
// no error, the response is complete
|
||||
return nil
|
||||
}
|
||||
|
||||
var wrapped *Error
|
||||
if errors.As(err, &wrapped) {
|
||||
// already a wire error, just use it
|
||||
return wrapped
|
||||
}
|
||||
|
||||
result := &Error{Message: err.Error()}
|
||||
if errors.As(err, &wrapped) {
|
||||
// if we wrapped a wire error, keep the code from the wrapped error
|
||||
// but the message from the outer error
|
||||
result.Code = wrapped.Code
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Notification is a request for which a response cannot occur, and as such
|
||||
// it has not ID.
|
||||
type Notification struct {
|
||||
// Method is a string containing the method name to invoke.
|
||||
method string
|
||||
|
||||
params json.RawMessage
|
||||
}
|
||||
|
||||
// make sure a Notification implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
|
||||
var (
|
||||
_ Request = (*Notification)(nil)
|
||||
_ json.Marshaler = (*Notification)(nil)
|
||||
_ json.Unmarshaler = (*Notification)(nil)
|
||||
)
|
||||
|
||||
// NewNotification constructs a new Notification message for the supplied
|
||||
// method and parameters.
|
||||
func NewNotification(method string, params any) (*Notification, error) {
|
||||
p, merr := marshalInterface(params)
|
||||
notify := &Notification{
|
||||
method: method,
|
||||
params: p,
|
||||
}
|
||||
return notify, merr
|
||||
}
|
||||
|
||||
// Method implements Request.
|
||||
func (n *Notification) Method() string { return n.method }
|
||||
|
||||
// Params implements Request.
|
||||
func (n *Notification) Params() json.RawMessage { return n.params }
|
||||
|
||||
// jsonrpc2Message implements Request.
|
||||
func (Notification) jsonrpc2Message() {}
|
||||
|
||||
// jsonrpc2Request implements Request.
|
||||
func (Notification) jsonrpc2Request() {}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (n Notification) MarshalJSON() ([]byte, error) {
|
||||
req := wireRequest{
|
||||
Method: n.method,
|
||||
Params: &n.params,
|
||||
}
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling notification: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (n *Notification) UnmarshalJSON(data []byte) error {
|
||||
var req wireRequest
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
if err := dec.Decode(&req); err != nil {
|
||||
return fmt.Errorf("unmarshaling notification: %w", err)
|
||||
}
|
||||
|
||||
n.method = req.Method
|
||||
if req.Params != nil {
|
||||
n.params = *req.Params
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeMessage decodes data to Message.
|
||||
func DecodeMessage(data []byte) (Message, error) {
|
||||
var msg combined
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
|
||||
}
|
||||
|
||||
if msg.Method == "" {
|
||||
// no method, should be a response
|
||||
if msg.ID == nil {
|
||||
return nil, ErrInvalidRequest
|
||||
}
|
||||
|
||||
resp := &Response{
|
||||
id: *msg.ID,
|
||||
}
|
||||
if msg.Error != nil {
|
||||
resp.err = msg.Error
|
||||
}
|
||||
if msg.Result != nil {
|
||||
resp.result = *msg.Result
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// has a method, must be a request
|
||||
if msg.ID == nil {
|
||||
// request with no ID is a notify
|
||||
notify := &Notification{
|
||||
method: msg.Method,
|
||||
}
|
||||
if msg.Params != nil {
|
||||
notify.params = *msg.Params
|
||||
}
|
||||
|
||||
return notify, nil
|
||||
}
|
||||
|
||||
// request with an ID, must be a call
|
||||
call := &Call{
|
||||
method: msg.Method,
|
||||
id: *msg.ID,
|
||||
}
|
||||
if msg.Params != nil {
|
||||
call.params = *msg.Params
|
||||
}
|
||||
|
||||
return call, nil
|
||||
}
|
||||
|
||||
// marshalInterface marshal obj to json.RawMessage.
|
||||
func marshalInterface(obj any) (json.RawMessage, error) {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return json.RawMessage{}, fmt.Errorf("failed to marshal json: %w", err)
|
||||
}
|
||||
return json.RawMessage(data), nil
|
||||
}
|
129
templ/lsp/jsonrpc2/serve.go
Normal file
129
templ/lsp/jsonrpc2/serve.go
Normal file
@@ -0,0 +1,129 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NOTE: This file provides an experimental API for serving multiple remote
|
||||
// jsonrpc2 clients over the network. For now, it is intentionally similar to
|
||||
// net/http, but that may change in the future as we figure out the correct
|
||||
// semantics.
|
||||
|
||||
// StreamServer is used to serve incoming jsonrpc2 clients communicating over
|
||||
// a newly created connection.
|
||||
type StreamServer interface {
|
||||
ServeStream(context.Context, Conn) error
|
||||
}
|
||||
|
||||
// ServerFunc is an adapter that implements the StreamServer interface
|
||||
// using an ordinary function.
|
||||
type ServerFunc func(context.Context, Conn) error
|
||||
|
||||
// ServeStream implements StreamServer.
|
||||
//
|
||||
// ServeStream calls f(ctx, s).
|
||||
func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error {
|
||||
return f(ctx, c)
|
||||
}
|
||||
|
||||
// HandlerServer returns a StreamServer that handles incoming streams using the
|
||||
// provided handler.
|
||||
func HandlerServer(h Handler) StreamServer {
|
||||
return ServerFunc(func(ctx context.Context, conn Conn) error {
|
||||
conn.Go(ctx, h)
|
||||
<-conn.Done()
|
||||
return conn.Err()
|
||||
})
|
||||
}
|
||||
|
||||
// ListenAndServe starts an jsonrpc2 server on the given address.
|
||||
//
|
||||
// If idleTimeout is non-zero, ListenAndServe exits after there are no clients for
|
||||
// this duration, otherwise it exits only on error.
|
||||
func ListenAndServe(ctx context.Context, network, addr string, server StreamServer, idleTimeout time.Duration) error {
|
||||
ln, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen %s:%s: %w", network, addr, err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
if network == "unix" {
|
||||
defer os.Remove(addr)
|
||||
}
|
||||
|
||||
return Serve(ctx, ln, server, idleTimeout)
|
||||
}
|
||||
|
||||
// Serve accepts incoming connections from the network, and handles them using
|
||||
// the provided server. If idleTimeout is non-zero, ListenAndServe exits after
|
||||
// there are no clients for this duration, otherwise it exits only on error.
|
||||
func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeout time.Duration) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
// Max duration: ~290 years; surely that's long enough.
|
||||
const forever = 1<<63 - 1
|
||||
if idleTimeout <= 0 {
|
||||
idleTimeout = forever
|
||||
}
|
||||
connTimer := time.NewTimer(idleTimeout)
|
||||
|
||||
newConns := make(chan net.Conn)
|
||||
doneListening := make(chan error)
|
||||
closedConns := make(chan error)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
nc, err := ln.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case doneListening <- fmt.Errorf("accept: %w", err):
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
newConns <- nc
|
||||
}
|
||||
}()
|
||||
|
||||
activeConns := 0
|
||||
for {
|
||||
select {
|
||||
case netConn := <-newConns:
|
||||
activeConns++
|
||||
connTimer.Stop()
|
||||
stream := NewStream(netConn)
|
||||
go func() {
|
||||
conn := NewConn(stream)
|
||||
closedConns <- server.ServeStream(ctx, conn)
|
||||
stream.Close()
|
||||
}()
|
||||
|
||||
case err := <-doneListening:
|
||||
return err
|
||||
|
||||
case <-closedConns:
|
||||
// if !isClosingError(err) {
|
||||
// }
|
||||
|
||||
activeConns--
|
||||
if activeConns == 0 {
|
||||
connTimer.Reset(idleTimeout)
|
||||
}
|
||||
|
||||
case <-connTimer.C:
|
||||
return ErrIdleTimeout
|
||||
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
60
templ/lsp/jsonrpc2/serve_test.go
Normal file
60
templ/lsp/jsonrpc2/serve_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
)
|
||||
|
||||
func TestIdleTimeout(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
connect := func() net.Conn {
|
||||
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 5*time.Second)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
server := jsonrpc2.HandlerServer(jsonrpc2.MethodNotFoundHandler)
|
||||
var (
|
||||
runErr error
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
runErr = jsonrpc2.Serve(ctx, ln, server, 100*time.Millisecond)
|
||||
}()
|
||||
|
||||
// Exercise some connection/disconnection patterns, and then assert that when
|
||||
// our timer fires, the server exits.
|
||||
conn1 := connect()
|
||||
conn2 := connect()
|
||||
conn1.Close()
|
||||
conn2.Close()
|
||||
conn3 := connect()
|
||||
conn3.Close()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if !errors.Is(runErr, jsonrpc2.ErrIdleTimeout) {
|
||||
t.Errorf("run() returned error %v, want %v", runErr, jsonrpc2.ErrIdleTimeout)
|
||||
}
|
||||
}
|
226
templ/lsp/jsonrpc2/stream.go
Normal file
226
templ/lsp/jsonrpc2/stream.go
Normal file
@@ -0,0 +1,226 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
stdjson "encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
// HdrContentLength is the HTTP header name of the length of the content part in bytes. This header is required.
|
||||
// This entity header indicates the size of the entity-body, in bytes, sent to the recipient.
|
||||
//
|
||||
// RFC 7230, section 3.3.2: Content-Length:
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3.2
|
||||
HdrContentLength = "Content-Length"
|
||||
|
||||
// HeaderContentType is the mime type of the content part. Defaults to "application/vscode-jsonrpc; charset=utf-8".
|
||||
// This entity header is used to indicate the media type of the resource.
|
||||
//
|
||||
// RFC 7231, section 3.1.1.5: Content-Type:
|
||||
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
|
||||
HdrContentType = "Content-Type"
|
||||
|
||||
// HeaderContentSeparator is the header and content part separator.
|
||||
HdrContentSeparator = "\r\n\r\n"
|
||||
)
|
||||
|
||||
// Framer wraps a network connection up into a Stream.
|
||||
//
|
||||
// It is responsible for the framing and encoding of messages into wire form.
|
||||
// NewRawStream and NewStream are implementations of a Framer.
|
||||
type Framer func(conn io.ReadWriteCloser) Stream
|
||||
|
||||
// Stream abstracts the transport mechanics from the JSON RPC protocol.
|
||||
//
|
||||
// A Conn reads and writes messages using the stream it was provided on
|
||||
// construction, and assumes that each call to Read or Write fully transfers
|
||||
// a single message, or returns an error.
|
||||
//
|
||||
// A stream is not safe for concurrent use, it is expected it will be used by
|
||||
// a single Conn in a safe manner.
|
||||
type Stream interface {
|
||||
// Read gets the next message from the stream.
|
||||
Read(context.Context) (Message, int64, error)
|
||||
|
||||
// Write sends a message to the stream.
|
||||
Write(context.Context, Message) (int64, error)
|
||||
|
||||
// Close closes the connection.
|
||||
// Any blocked Read or Write operations will be unblocked and return errors.
|
||||
Close() error
|
||||
}
|
||||
|
||||
type rawStream struct {
|
||||
conn io.ReadWriteCloser
|
||||
in *stdjson.Decoder
|
||||
}
|
||||
|
||||
// NewRawStream returns a Stream built on top of a io.ReadWriteCloser.
|
||||
//
|
||||
// The messages are sent with no wrapping, and rely on json decode consistency
|
||||
// to determine message boundaries.
|
||||
func NewRawStream(conn io.ReadWriteCloser) Stream {
|
||||
return &rawStream{
|
||||
conn: conn,
|
||||
in: stdjson.NewDecoder(conn), // TODO(zchee): why test fail using segmentio json.Decoder?
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements Stream.Read.
|
||||
func (s *rawStream) Read(ctx context.Context) (Message, int64, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
var raw stdjson.RawMessage
|
||||
if err := s.in.Decode(&raw); err != nil {
|
||||
return nil, 0, fmt.Errorf("decoding raw message: %w", err)
|
||||
}
|
||||
|
||||
msg, err := DecodeMessage(raw)
|
||||
return msg, int64(len(raw)), err
|
||||
}
|
||||
|
||||
// Write implements Stream.Write.
|
||||
func (s *rawStream) Write(ctx context.Context, msg Message) (int64, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("marshaling message: %w", err)
|
||||
}
|
||||
|
||||
n, err := s.conn.Write(data)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("write to stream: %w", err)
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
// Close implements Stream.Close.
|
||||
func (s *rawStream) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
conn io.ReadWriteCloser
|
||||
in *bufio.Reader
|
||||
}
|
||||
|
||||
// NewStream returns a Stream built on top of a io.ReadWriteCloser.
|
||||
//
|
||||
// The messages are sent with HTTP content length and MIME type headers.
|
||||
// This is the format used by LSP and others.
|
||||
func NewStream(conn io.ReadWriteCloser) Stream {
|
||||
return &stream{
|
||||
conn: conn,
|
||||
in: bufio.NewReader(conn),
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements Stream.Read.
|
||||
func (s *stream) Read(ctx context.Context) (Message, int64, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
var total int64
|
||||
var length int64
|
||||
// read the header, stop on the first empty line
|
||||
for {
|
||||
line, err := s.in.ReadString('\n')
|
||||
total += int64(len(line))
|
||||
if err != nil {
|
||||
return nil, total, fmt.Errorf("failed reading header line: %w", err)
|
||||
}
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
// check we have a header line
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
|
||||
colon := strings.IndexRune(line, ':')
|
||||
if colon < 0 {
|
||||
return nil, total, fmt.Errorf("invalid header line %q", line)
|
||||
}
|
||||
|
||||
name, value := line[:colon], strings.TrimSpace(line[colon+1:])
|
||||
switch name {
|
||||
case HdrContentLength:
|
||||
if length, err = strconv.ParseInt(value, 10, 32); err != nil {
|
||||
return nil, total, fmt.Errorf("failed parsing %s: %v: %w", HdrContentLength, value, err)
|
||||
}
|
||||
if length <= 0 {
|
||||
return nil, total, fmt.Errorf("invalid %s: %v", HdrContentLength, length)
|
||||
}
|
||||
default:
|
||||
// ignoring unknown headers
|
||||
}
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return nil, total, fmt.Errorf("missing %s header", HdrContentLength)
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
if _, err := io.ReadFull(s.in, data); err != nil {
|
||||
return nil, total, fmt.Errorf("read full of data: %w", err)
|
||||
}
|
||||
|
||||
total += length
|
||||
msg, err := DecodeMessage(data)
|
||||
return msg, total, err
|
||||
}
|
||||
|
||||
// Write implements Stream.Write.
|
||||
func (s *stream) Write(ctx context.Context, msg Message) (int64, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("marshaling message: %w", err)
|
||||
}
|
||||
|
||||
n, err := fmt.Fprintf(s.conn, "%s: %v%s", HdrContentLength, len(data), HdrContentSeparator)
|
||||
total := int64(n)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("write data to conn: %w", err)
|
||||
}
|
||||
|
||||
n, err = s.conn.Write(data)
|
||||
total += int64(n)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("write data to conn: %w", err)
|
||||
}
|
||||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// Close implements Stream.Close.
|
||||
func (s *stream) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
140
templ/lsp/jsonrpc2/wire.go
Normal file
140
templ/lsp/jsonrpc2/wire.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Version represents a JSON-RPC version.
|
||||
const Version = "2.0"
|
||||
|
||||
// version is a special 0 sized struct that encodes as the jsonrpc version tag.
|
||||
//
|
||||
// It will fail during decode if it is not the correct version tag in the stream.
|
||||
type version struct{}
|
||||
|
||||
// compile time check whether the version implements a json.Marshaler and json.Unmarshaler interfaces.
|
||||
var (
|
||||
_ json.Marshaler = (*version)(nil)
|
||||
_ json.Unmarshaler = (*version)(nil)
|
||||
)
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (version) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(Version)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (version) UnmarshalJSON(data []byte) error {
|
||||
version := ""
|
||||
if err := json.Unmarshal(data, &version); err != nil {
|
||||
return fmt.Errorf("failed to Unmarshal: %w", err)
|
||||
}
|
||||
if version != Version {
|
||||
return fmt.Errorf("invalid RPC version %v", version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID is a Request identifier.
|
||||
//
|
||||
// Only one of either the Name or Number members will be set, using the
|
||||
// number form if the Name is the empty string.
|
||||
type ID struct {
|
||||
name string
|
||||
number int32
|
||||
}
|
||||
|
||||
// compile time check whether the ID implements a fmt.Formatter, json.Marshaler and json.Unmarshaler interfaces.
|
||||
var (
|
||||
_ fmt.Formatter = (*ID)(nil)
|
||||
_ json.Marshaler = (*ID)(nil)
|
||||
_ json.Unmarshaler = (*ID)(nil)
|
||||
)
|
||||
|
||||
// NewNumberID returns a new number request ID.
|
||||
func NewNumberID(v int32) ID { return ID{number: v} }
|
||||
|
||||
// NewStringID returns a new string request ID.
|
||||
func NewStringID(v string) ID { return ID{name: v} }
|
||||
|
||||
// Format writes the ID to the formatter.
|
||||
//
|
||||
// If the rune is q the representation is non ambiguous,
|
||||
// string forms are quoted, number forms are preceded by a #.
|
||||
func (id ID) Format(f fmt.State, r rune) {
|
||||
numF, strF := `%d`, `%s`
|
||||
if r == 'q' {
|
||||
numF, strF = `#%d`, `%q`
|
||||
}
|
||||
|
||||
switch {
|
||||
case id.name != "":
|
||||
fmt.Fprintf(f, strF, id.name)
|
||||
default:
|
||||
fmt.Fprintf(f, numF, id.number)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (id *ID) MarshalJSON() ([]byte, error) {
|
||||
if id.name != "" {
|
||||
return json.Marshal(id.name)
|
||||
}
|
||||
return json.Marshal(id.number)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (id *ID) UnmarshalJSON(data []byte) error {
|
||||
*id = ID{}
|
||||
if err := json.Unmarshal(data, &id.number); err == nil {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, &id.name)
|
||||
}
|
||||
|
||||
// wireRequest is sent to a server to represent a Call or Notify operaton.
|
||||
type wireRequest struct {
|
||||
// VersionTag is always encoded as the string "2.0"
|
||||
VersionTag version `json:"jsonrpc"`
|
||||
// Method is a string containing the method name to invoke.
|
||||
Method string `json:"method"`
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
Params *json.RawMessage `json:"params,omitempty"`
|
||||
// The id of this request, used to tie the Response back to the request.
|
||||
// Will be either a string or a number. If not set, the Request is a notify,
|
||||
// and no response is possible.
|
||||
ID *ID `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// wireResponse is a reply to a Request.
|
||||
//
|
||||
// It will always have the ID field set to tie it back to a request, and will
|
||||
// have either the Result or Error fields set depending on whether it is a
|
||||
// success or failure wireResponse.
|
||||
type wireResponse struct {
|
||||
// VersionTag is always encoded as the string "2.0"
|
||||
VersionTag version `json:"jsonrpc"`
|
||||
// Result is the response value, and is required on success.
|
||||
Result *json.RawMessage `json:"result,omitempty"`
|
||||
// Error is a structured error response if the call fails.
|
||||
Error *Error `json:"error,omitempty"`
|
||||
// ID must be set and is the identifier of the Request this is a response to.
|
||||
ID *ID `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// combined has all the fields of both Request and Response.
|
||||
//
|
||||
// We can decode this and then work out which it is.
|
||||
type combined struct {
|
||||
VersionTag version `json:"jsonrpc"`
|
||||
ID *ID `json:"id,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Params *json.RawMessage `json:"params,omitempty"`
|
||||
Result *json.RawMessage `json:"result,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
}
|
156
templ/lsp/jsonrpc2/wire_test.go
Normal file
156
templ/lsp/jsonrpc2/wire_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package jsonrpc2_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
)
|
||||
|
||||
var wireIDTestData = []struct {
|
||||
name string
|
||||
id jsonrpc2.ID
|
||||
encoded []byte
|
||||
plain string
|
||||
quoted string
|
||||
}{
|
||||
{
|
||||
name: `empty`,
|
||||
encoded: []byte(`0`),
|
||||
plain: `0`,
|
||||
quoted: `#0`,
|
||||
}, {
|
||||
name: `number`,
|
||||
id: jsonrpc2.NewNumberID(43),
|
||||
encoded: []byte(`43`),
|
||||
plain: `43`,
|
||||
quoted: `#43`,
|
||||
}, {
|
||||
name: `string`,
|
||||
id: jsonrpc2.NewStringID("life"),
|
||||
encoded: []byte(`"life"`),
|
||||
plain: `life`,
|
||||
quoted: `"life"`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestIDFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range wireIDTestData {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := fmt.Sprint(tt.id); got != tt.plain {
|
||||
t.Errorf("got %s expected %s", got, tt.plain)
|
||||
}
|
||||
if got := fmt.Sprintf("%q", tt.id); got != tt.quoted {
|
||||
t.Errorf("got %s want %s", got, tt.quoted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range wireIDTestData {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := json.Marshal(&tt.id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkJSON(t, data, tt.encoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range wireIDTestData {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got *jsonrpc2.ID
|
||||
dec := json.NewDecoder(bytes.NewReader(tt.encoded))
|
||||
if err := dec.Decode(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reflect.ValueOf(&got).IsZero() {
|
||||
t.Fatalf("got nil want %s", tt.id)
|
||||
}
|
||||
|
||||
if *got != tt.id {
|
||||
t.Fatalf("got %s want %s", got, tt.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
b, err := json.Marshal(jsonrpc2.NewError(0, ""))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkJSON(t, b, []byte(`{
|
||||
"code": 0,
|
||||
"message": ""
|
||||
}`))
|
||||
}
|
||||
|
||||
func TestErrorResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// originally reported in #39719, this checks that result is not present if
|
||||
// it is an error response
|
||||
r, _ := jsonrpc2.NewResponse(jsonrpc2.NewNumberID(3), nil, fmt.Errorf("computing fix edits"))
|
||||
data, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkJSON(t, data, []byte(`{
|
||||
"jsonrpc":"2.0",
|
||||
"error":{
|
||||
"code":0,
|
||||
"message":"computing fix edits"
|
||||
},
|
||||
"id":3
|
||||
}`))
|
||||
}
|
||||
|
||||
func checkJSON(t *testing.T, got, want []byte) {
|
||||
t.Helper()
|
||||
|
||||
// compare the compact form, to allow for formatting differences
|
||||
g := &bytes.Buffer{}
|
||||
if err := json.Compact(g, got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
if err := json.Compact(w, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if g.String() != w.String() {
|
||||
t.Fatalf("Got:\n%s\nWant:\n%s", g, w)
|
||||
}
|
||||
}
|
95
templ/lsp/protocol/base.go
Normal file
95
templ/lsp/protocol/base.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CancelParams params of cancelRequest.
|
||||
type CancelParams struct {
|
||||
// ID is the request id to cancel.
|
||||
ID any `json:"id"` // int32 | string
|
||||
}
|
||||
|
||||
// ProgressParams params of Progress netification.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ProgressParams struct {
|
||||
// Token is the progress token provided by the client or server.
|
||||
Token ProgressToken `json:"token"`
|
||||
|
||||
// Value is the progress data.
|
||||
Value any `json:"value"`
|
||||
}
|
||||
|
||||
// ProgressToken is the progress token provided by the client or server.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ProgressToken struct {
|
||||
name string
|
||||
number int32
|
||||
}
|
||||
|
||||
// compile time check whether the ProgressToken implements a fmt.Formatter, fmt.Stringer, json.Marshaler and json.Unmarshaler interfaces.
|
||||
var (
|
||||
_ fmt.Formatter = (*ProgressToken)(nil)
|
||||
_ fmt.Stringer = (*ProgressToken)(nil)
|
||||
_ json.Marshaler = (*ProgressToken)(nil)
|
||||
_ json.Unmarshaler = (*ProgressToken)(nil)
|
||||
)
|
||||
|
||||
// NewProgressToken returns a new ProgressToken.
|
||||
func NewProgressToken(s string) *ProgressToken {
|
||||
return &ProgressToken{name: s}
|
||||
}
|
||||
|
||||
// NewNumberProgressToken returns a new number ProgressToken.
|
||||
func NewNumberProgressToken(n int32) *ProgressToken {
|
||||
return &ProgressToken{number: n}
|
||||
}
|
||||
|
||||
// Format writes the ProgressToken to the formatter.
|
||||
//
|
||||
// If the rune is q the representation is non ambiguous,
|
||||
// string forms are quoted.
|
||||
func (v ProgressToken) Format(f fmt.State, r rune) {
|
||||
const numF = `%d`
|
||||
strF := `%s`
|
||||
if r == 'q' {
|
||||
strF = `%q`
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.name != "":
|
||||
fmt.Fprintf(f, strF, v.name)
|
||||
default:
|
||||
fmt.Fprintf(f, numF, v.number)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the ProgressToken.
|
||||
func (v ProgressToken) String() string {
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v *ProgressToken) MarshalJSON() ([]byte, error) {
|
||||
if v.name != "" {
|
||||
return json.Marshal(v.name)
|
||||
}
|
||||
|
||||
return json.Marshal(v.number)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (v *ProgressToken) UnmarshalJSON(data []byte) error {
|
||||
*v = ProgressToken{}
|
||||
if err := json.Unmarshal(data, &v.number); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, &v.name)
|
||||
}
|
186
templ/lsp/protocol/base_test.go
Normal file
186
templ/lsp/protocol/base_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
)
|
||||
|
||||
func TestCancelParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const want = `{"id":"testID"}`
|
||||
wantType := CancelParams{
|
||||
ID: "testID",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field CancelParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want CancelParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got CancelParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestProgressParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const wantWorkDoneToken = "156edea9-9d8d-422f-b7ee-81a84594afbb"
|
||||
const want = `{"token":"` + wantWorkDoneToken + `","value":"testValue"}`
|
||||
|
||||
token := NewProgressToken(wantWorkDoneToken)
|
||||
wantType := ProgressParams{
|
||||
Token: *token,
|
||||
Value: "testValue",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field ProgressParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want ProgressParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got ProgressParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreFields(ProgressParams{}, "Token")); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
|
||||
if token := got.Token; !reflect.ValueOf(token).IsZero() {
|
||||
if diff := cmp.Diff(fmt.Sprint(token), wantWorkDoneToken); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
705
templ/lsp/protocol/basic.go
Normal file
705
templ/lsp/protocol/basic.go
Normal file
@@ -0,0 +1,705 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/a-h/templ/lsp/uri"
|
||||
)
|
||||
|
||||
// DocumentURI represents the URI of a document.
|
||||
//
|
||||
// Many of the interfaces contain fields that correspond to the URI of a document.
|
||||
// For clarity, the type of such a field is declared as a DocumentURI.
|
||||
// Over the wire, it will still be transferred as a string, but this guarantees
|
||||
// that the contents of that string can be parsed as a valid URI.
|
||||
type DocumentURI = uri.URI
|
||||
|
||||
// URI a tagging interface for normal non document URIs.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type URI = uri.URI
|
||||
|
||||
// EOL denotes represents the character offset.
|
||||
var EOL = []string{"\n", "\r\n", "\r"}
|
||||
|
||||
// Position represents a text document expressed as zero-based line and zero-based character offset.
|
||||
//
|
||||
// The offsets are based on a UTF-16 string representation.
|
||||
// So a string of the form "a𐐀b" the character offset of the character "a" is 0,
|
||||
// the character offset of "𐐀" is 1 and the character offset of "b" is 3 since 𐐀 is represented using two code
|
||||
// units in UTF-16.
|
||||
//
|
||||
// Positions are line end character agnostic. So you can not specify a position that
|
||||
// denotes "\r|\n" or "\n|" where "|" represents the character offset.
|
||||
//
|
||||
// Position is between two characters like an "insert" cursor in a editor.
|
||||
// Special values like for example "-1" to denote the end of a line are not supported.
|
||||
type Position struct {
|
||||
// Line position in a document (zero-based).
|
||||
//
|
||||
// If a line number is greater than the number of lines in a document, it defaults back to the number of lines in
|
||||
// the document.
|
||||
// If a line number is negative, it defaults to 0.
|
||||
Line uint32 `json:"line"`
|
||||
|
||||
// Character offset on a line in a document (zero-based).
|
||||
//
|
||||
// Assuming that the line is represented as a string, the Character value represents the gap between the
|
||||
// "character" and "character + 1".
|
||||
//
|
||||
// If the character value is greater than the line length it defaults back to the line length.
|
||||
// If a line number is negative, it defaults to 0.
|
||||
Character uint32 `json:"character"`
|
||||
}
|
||||
|
||||
// Range represents a text document expressed as (zero-based) start and end positions.
|
||||
//
|
||||
// A range is comparable to a selection in an editor. Therefore the end position is exclusive.
|
||||
// If you want to specify a range that contains a line including the line ending character(s) then use an end position
|
||||
// denoting the start of the next line.
|
||||
type Range struct {
|
||||
// Start is the range's start position.
|
||||
Start Position `json:"start"`
|
||||
|
||||
// End is the range's end position.
|
||||
End Position `json:"end"`
|
||||
}
|
||||
|
||||
// Location represents a location inside a resource, such as a line inside a text file.
|
||||
type Location struct {
|
||||
URI DocumentURI `json:"uri"`
|
||||
Range Range `json:"range"`
|
||||
}
|
||||
|
||||
// LocationLink represents a link between a source and a target location.
|
||||
type LocationLink struct {
|
||||
// OriginSelectionRange span of the origin of this link.
|
||||
//
|
||||
// Used as the underlined span for mouse interaction. Defaults to the word range at the mouse position.
|
||||
OriginSelectionRange *Range `json:"originSelectionRange,omitempty"`
|
||||
|
||||
// TargetURI is the target resource identifier of this link.
|
||||
TargetURI DocumentURI `json:"targetUri"`
|
||||
|
||||
// TargetRange is the full target range of this link.
|
||||
//
|
||||
// If the target for example is a symbol then target range is the range enclosing this symbol not including
|
||||
// leading/trailing whitespace but everything else like comments.
|
||||
//
|
||||
// This information is typically used to highlight the range in the editor.
|
||||
TargetRange Range `json:"targetRange"`
|
||||
|
||||
// TargetSelectionRange is the range that should be selected and revealed when this link is being followed,
|
||||
// e.g the name of a function.
|
||||
//
|
||||
// Must be contained by the the TargetRange. See also DocumentSymbol#range
|
||||
TargetSelectionRange Range `json:"targetSelectionRange"`
|
||||
}
|
||||
|
||||
// Command represents a reference to a command. Provides a title which will be used to represent a command in the UI.
|
||||
//
|
||||
// Commands are identified by a string identifier.
|
||||
// The recommended way to handle commands is to implement their execution on the server side if the client and
|
||||
// server provides the corresponding capabilities.
|
||||
//
|
||||
// Alternatively the tool extension code could handle the command. The protocol currently doesn't specify
|
||||
// a set of well-known commands.
|
||||
type Command struct {
|
||||
// Title of the command, like `save`.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Command is the identifier of the actual command handler.
|
||||
Command string `json:"command"`
|
||||
|
||||
// Arguments that the command handler should be invoked with.
|
||||
Arguments []any `json:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// TextEdit is a textual edit applicable to a text document.
|
||||
type TextEdit struct {
|
||||
// Range is the range of the text document to be manipulated.
|
||||
//
|
||||
// To insert text into a document create a range where start == end.
|
||||
Range Range `json:"range"`
|
||||
|
||||
// NewText is the string to be inserted. For delete operations use an
|
||||
// empty string.
|
||||
NewText string `json:"newText"`
|
||||
}
|
||||
|
||||
// ChangeAnnotation is the additional information that describes document changes.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type ChangeAnnotation struct {
|
||||
// Label a human-readable string describing the actual change.
|
||||
// The string is rendered prominent in the user interface.
|
||||
Label string `json:"label"`
|
||||
|
||||
// NeedsConfirmation is a flag which indicates that user confirmation is needed
|
||||
// before applying the change.
|
||||
NeedsConfirmation bool `json:"needsConfirmation,omitempty"`
|
||||
|
||||
// Description is a human-readable string which is rendered less prominent in
|
||||
// the user interface.
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// ChangeAnnotationIdentifier an identifier referring to a change annotation managed by a workspace
|
||||
// edit.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type ChangeAnnotationIdentifier string
|
||||
|
||||
// AnnotatedTextEdit is a special text edit with an additional change annotation.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type AnnotatedTextEdit struct {
|
||||
TextEdit
|
||||
|
||||
// AnnotationID is the actual annotation identifier.
|
||||
AnnotationID ChangeAnnotationIdentifier `json:"annotationId"`
|
||||
}
|
||||
|
||||
// TextDocumentEdit describes textual changes on a single text document.
|
||||
//
|
||||
// The TextDocument is referred to as a OptionalVersionedTextDocumentIdentifier to allow clients to check the
|
||||
// text document version before an edit is applied.
|
||||
//
|
||||
// TextDocumentEdit describes all changes on a version "Si" and after they are applied move the document to
|
||||
// version "Si+1".
|
||||
// So the creator of a TextDocumentEdit doesn't need to sort the array or do any kind of ordering. However the
|
||||
// edits must be non overlapping.
|
||||
type TextDocumentEdit struct {
|
||||
// TextDocument is the text document to change.
|
||||
TextDocument OptionalVersionedTextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// Edits is the edits to be applied.
|
||||
//
|
||||
// @since 3.16.0 - support for AnnotatedTextEdit.
|
||||
// This is guarded by the client capability Workspace.WorkspaceEdit.ChangeAnnotationSupport.
|
||||
Edits []TextEdit `json:"edits"` // []TextEdit | []AnnotatedTextEdit
|
||||
}
|
||||
|
||||
// ResourceOperationKind is the file event type.
|
||||
type ResourceOperationKind string
|
||||
|
||||
const (
|
||||
// CreateResourceOperation supports creating new files and folders.
|
||||
CreateResourceOperation ResourceOperationKind = "create"
|
||||
|
||||
// RenameResourceOperation supports renaming existing files and folders.
|
||||
RenameResourceOperation ResourceOperationKind = "rename"
|
||||
|
||||
// DeleteResourceOperation supports deleting existing files and folders.
|
||||
DeleteResourceOperation ResourceOperationKind = "delete"
|
||||
)
|
||||
|
||||
// CreateFileOptions represents an options to create a file.
|
||||
type CreateFileOptions struct {
|
||||
// Overwrite existing file. Overwrite wins over `ignoreIfExists`.
|
||||
Overwrite bool `json:"overwrite,omitempty"`
|
||||
|
||||
// IgnoreIfExists ignore if exists.
|
||||
IgnoreIfExists bool `json:"ignoreIfExists,omitempty"`
|
||||
}
|
||||
|
||||
// CreateFile represents a create file operation.
|
||||
type CreateFile struct {
|
||||
// Kind a create.
|
||||
Kind ResourceOperationKind `json:"kind"` // should be `create`
|
||||
|
||||
// URI is the resource to create.
|
||||
URI DocumentURI `json:"uri"`
|
||||
|
||||
// Options additional options.
|
||||
Options *CreateFileOptions `json:"options,omitempty"`
|
||||
|
||||
// AnnotationID an optional annotation identifier describing the operation.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
AnnotationID ChangeAnnotationIdentifier `json:"annotationId,omitempty"`
|
||||
}
|
||||
|
||||
// RenameFileOptions represents a rename file options.
|
||||
type RenameFileOptions struct {
|
||||
// Overwrite target if existing. Overwrite wins over `ignoreIfExists`.
|
||||
Overwrite bool `json:"overwrite,omitempty"`
|
||||
|
||||
// IgnoreIfExists ignores if target exists.
|
||||
IgnoreIfExists bool `json:"ignoreIfExists,omitempty"`
|
||||
}
|
||||
|
||||
// RenameFile represents a rename file operation.
|
||||
type RenameFile struct {
|
||||
// Kind a rename.
|
||||
Kind ResourceOperationKind `json:"kind"` // should be `rename`
|
||||
|
||||
// OldURI is the old (existing) location.
|
||||
OldURI DocumentURI `json:"oldUri"`
|
||||
|
||||
// NewURI is the new location.
|
||||
NewURI DocumentURI `json:"newUri"`
|
||||
|
||||
// Options rename options.
|
||||
Options *RenameFileOptions `json:"options,omitempty"`
|
||||
|
||||
// AnnotationID an optional annotation identifier describing the operation.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
AnnotationID ChangeAnnotationIdentifier `json:"annotationId,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteFileOptions represents a delete file options.
|
||||
type DeleteFileOptions struct {
|
||||
// Recursive delete the content recursively if a folder is denoted.
|
||||
Recursive bool `json:"recursive,omitempty"`
|
||||
|
||||
// IgnoreIfNotExists ignore the operation if the file doesn't exist.
|
||||
IgnoreIfNotExists bool `json:"ignoreIfNotExists,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteFile represents a delete file operation.
|
||||
type DeleteFile struct {
|
||||
// Kind is a delete.
|
||||
Kind ResourceOperationKind `json:"kind"` // should be `delete`
|
||||
|
||||
// URI is the file to delete.
|
||||
URI DocumentURI `json:"uri"`
|
||||
|
||||
// Options delete options.
|
||||
Options *DeleteFileOptions `json:"options,omitempty"`
|
||||
|
||||
// AnnotationID an optional annotation identifier describing the operation.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
AnnotationID ChangeAnnotationIdentifier `json:"annotationId,omitempty"`
|
||||
}
|
||||
|
||||
// WorkspaceEdit represent a changes to many resources managed in the workspace.
|
||||
//
|
||||
// The edit should either provide changes or documentChanges.
|
||||
// If the client can handle versioned document edits and if documentChanges are present, the latter are preferred over
|
||||
// changes.
|
||||
type WorkspaceEdit struct {
|
||||
// Changes holds changes to existing resources.
|
||||
Changes map[DocumentURI][]TextEdit `json:"changes,omitempty"`
|
||||
|
||||
// DocumentChanges depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
|
||||
// are either an array of `TextDocumentEdit`s to express changes to n different text documents
|
||||
// where each text document edit addresses a specific version of a text document. Or it can contain
|
||||
// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
|
||||
//
|
||||
// Whether a client supports versioned document edits is expressed via
|
||||
// `workspace.workspaceEdit.documentChanges` client capability.
|
||||
//
|
||||
// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
|
||||
// only plain `TextEdit`s using the `changes` property are supported.
|
||||
DocumentChanges []TextDocumentEdit `json:"documentChanges,omitempty"`
|
||||
|
||||
// ChangeAnnotations is a map of change annotations that can be referenced in
|
||||
// "AnnotatedTextEdit"s or create, rename and delete file / folder
|
||||
// operations.
|
||||
//
|
||||
// Whether clients honor this property depends on the client capability
|
||||
// "workspace.changeAnnotationSupport".
|
||||
//
|
||||
// @since 3.16.0.
|
||||
ChangeAnnotations map[ChangeAnnotationIdentifier]ChangeAnnotation `json:"changeAnnotations,omitempty"`
|
||||
}
|
||||
|
||||
// TextDocumentIdentifier indicates the using a URI. On the protocol level, URIs are passed as strings.
|
||||
type TextDocumentIdentifier struct {
|
||||
// URI is the text document's URI.
|
||||
URI DocumentURI `json:"uri"`
|
||||
}
|
||||
|
||||
// TextDocumentItem represent an item to transfer a text document from the client to the server.
|
||||
type TextDocumentItem struct {
|
||||
// URI is the text document's URI.
|
||||
URI DocumentURI `json:"uri"`
|
||||
|
||||
// LanguageID is the text document's language identifier.
|
||||
LanguageID LanguageIdentifier `json:"languageId"`
|
||||
|
||||
// Version is the version number of this document (it will increase after each
|
||||
// change, including undo/redo).
|
||||
Version int32 `json:"version"`
|
||||
|
||||
// Text is the content of the opened text document.
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// LanguageIdentifier represent a text document's language identifier.
|
||||
type LanguageIdentifier string
|
||||
|
||||
const (
|
||||
// ABAPLanguage ABAP Language.
|
||||
ABAPLanguage LanguageIdentifier = "abap"
|
||||
|
||||
// BatLanguage Windows Bat Language.
|
||||
BatLanguage LanguageIdentifier = "bat"
|
||||
|
||||
// BibtexLanguage BibTeX Language.
|
||||
BibtexLanguage LanguageIdentifier = "bibtex"
|
||||
|
||||
// ClojureLanguage Clojure Language.
|
||||
ClojureLanguage LanguageIdentifier = "clojure"
|
||||
|
||||
// CoffeescriptLanguage CoffeeScript Language.
|
||||
CoffeeScriptLanguage LanguageIdentifier = "coffeescript"
|
||||
|
||||
// CLanguage C Language.
|
||||
CLanguage LanguageIdentifier = "c"
|
||||
|
||||
// CppLanguage C++ Language.
|
||||
CppLanguage LanguageIdentifier = "cpp"
|
||||
|
||||
// CsharpLanguage C# Language.
|
||||
CsharpLanguage LanguageIdentifier = "csharp"
|
||||
|
||||
// CSSLanguage CSS Language.
|
||||
CSSLanguage LanguageIdentifier = "css"
|
||||
|
||||
// DiffLanguage Diff Language.
|
||||
DiffLanguage LanguageIdentifier = "diff"
|
||||
|
||||
// DartLanguage Dart Language.
|
||||
DartLanguage LanguageIdentifier = "dart"
|
||||
|
||||
// DockerfileLanguage Dockerfile Language.
|
||||
DockerfileLanguage LanguageIdentifier = "dockerfile"
|
||||
|
||||
// ElixirLanguage Elixir Language.
|
||||
ElixirLanguage LanguageIdentifier = "elixir"
|
||||
|
||||
// ErlangLanguage Erlang Language.
|
||||
ErlangLanguage LanguageIdentifier = "erlang"
|
||||
|
||||
// FsharpLanguage F# Language.
|
||||
FsharpLanguage LanguageIdentifier = "fsharp"
|
||||
|
||||
// GitCommitLanguage Git Language.
|
||||
GitCommitLanguage LanguageIdentifier = "git-commit"
|
||||
|
||||
// GitRebaseLanguage Git Language.
|
||||
GitRebaseLanguage LanguageIdentifier = "git-rebase"
|
||||
|
||||
// GoLanguage Go Language.
|
||||
GoLanguage LanguageIdentifier = "go"
|
||||
|
||||
// GroovyLanguage Groovy Language.
|
||||
GroovyLanguage LanguageIdentifier = "groovy"
|
||||
|
||||
// HandlebarsLanguage Handlebars Language.
|
||||
HandlebarsLanguage LanguageIdentifier = "handlebars"
|
||||
|
||||
// HTMLLanguage HTML Language.
|
||||
HTMLLanguage LanguageIdentifier = "html"
|
||||
|
||||
// IniLanguage Ini Language.
|
||||
IniLanguage LanguageIdentifier = "ini"
|
||||
|
||||
// JavaLanguage Java Language.
|
||||
JavaLanguage LanguageIdentifier = "java"
|
||||
|
||||
// JavaScriptLanguage JavaScript Language.
|
||||
JavaScriptLanguage LanguageIdentifier = "javascript"
|
||||
|
||||
// JavaScriptReactLanguage JavaScript React Language.
|
||||
JavaScriptReactLanguage LanguageIdentifier = "javascriptreact"
|
||||
|
||||
// JSONLanguage JSON Language.
|
||||
JSONLanguage LanguageIdentifier = "json"
|
||||
|
||||
// LatexLanguage LaTeX Language.
|
||||
LatexLanguage LanguageIdentifier = "latex"
|
||||
|
||||
// LessLanguage Less Language.
|
||||
LessLanguage LanguageIdentifier = "less"
|
||||
|
||||
// LuaLanguage Lua Language.
|
||||
LuaLanguage LanguageIdentifier = "lua"
|
||||
|
||||
// MakefileLanguage Makefile Language.
|
||||
MakefileLanguage LanguageIdentifier = "makefile"
|
||||
|
||||
// MarkdownLanguage Markdown Language.
|
||||
MarkdownLanguage LanguageIdentifier = "markdown"
|
||||
|
||||
// ObjectiveCLanguage Objective-C Language.
|
||||
ObjectiveCLanguage LanguageIdentifier = "objective-c"
|
||||
|
||||
// ObjectiveCppLanguage Objective-C++ Language.
|
||||
ObjectiveCppLanguage LanguageIdentifier = "objective-cpp"
|
||||
|
||||
// PerlLanguage Perl Language.
|
||||
PerlLanguage LanguageIdentifier = "perl"
|
||||
|
||||
// Perl6Language Perl Language.
|
||||
Perl6Language LanguageIdentifier = "perl6"
|
||||
|
||||
// PHPLanguage PHP Language.
|
||||
PHPLanguage LanguageIdentifier = "php"
|
||||
|
||||
// PowershellLanguage Powershell Language.
|
||||
PowershellLanguage LanguageIdentifier = "powershell"
|
||||
|
||||
// JadeLanguage Pug Language.
|
||||
JadeLanguage LanguageIdentifier = "jade"
|
||||
|
||||
// PythonLanguage Python Language.
|
||||
PythonLanguage LanguageIdentifier = "python"
|
||||
|
||||
// RLanguage R Language.
|
||||
RLanguage LanguageIdentifier = "r"
|
||||
|
||||
// RazorLanguage Razor(cshtml) Language.
|
||||
RazorLanguage LanguageIdentifier = "razor"
|
||||
|
||||
// RubyLanguage Ruby Language.
|
||||
RubyLanguage LanguageIdentifier = "ruby"
|
||||
|
||||
// RustLanguage Rust Language.
|
||||
RustLanguage LanguageIdentifier = "rust"
|
||||
|
||||
// SCSSLanguage SCSS Languages syntax using curly brackets.
|
||||
SCSSLanguage LanguageIdentifier = "scss"
|
||||
|
||||
// SASSLanguage SCSS Languages indented syntax.
|
||||
SASSLanguage LanguageIdentifier = "sass"
|
||||
|
||||
// ScalaLanguage Scala Language.
|
||||
ScalaLanguage LanguageIdentifier = "scala"
|
||||
|
||||
// ShaderlabLanguage ShaderLab Language.
|
||||
ShaderlabLanguage LanguageIdentifier = "shaderlab"
|
||||
|
||||
// ShellscriptLanguage Shell Script (Bash) Language.
|
||||
ShellscriptLanguage LanguageIdentifier = "shellscript"
|
||||
|
||||
// SQLLanguage SQL Language.
|
||||
SQLLanguage LanguageIdentifier = "sql"
|
||||
|
||||
// SwiftLanguage Swift Language.
|
||||
SwiftLanguage LanguageIdentifier = "swift"
|
||||
|
||||
// TypeScriptLanguage TypeScript Language.
|
||||
TypeScriptLanguage LanguageIdentifier = "typescript"
|
||||
|
||||
// TypeScriptReactLanguage TypeScript React Language.
|
||||
TypeScriptReactLanguage LanguageIdentifier = "typescriptreact"
|
||||
|
||||
// TeXLanguage TeX Language.
|
||||
TeXLanguage LanguageIdentifier = "tex"
|
||||
|
||||
// VBLanguage Visual Basic Language.
|
||||
VBLanguage LanguageIdentifier = "vb"
|
||||
|
||||
// XMLLanguage XML Language.
|
||||
XMLLanguage LanguageIdentifier = "xml"
|
||||
|
||||
// XslLanguage XSL Language.
|
||||
XslLanguage LanguageIdentifier = "xsl"
|
||||
|
||||
// YamlLanguage YAML Language.
|
||||
YamlLanguage LanguageIdentifier = "yaml"
|
||||
)
|
||||
|
||||
// languageIdentifierMap map of LanguageIdentifiers.
|
||||
var languageIdentifierMap = map[string]LanguageIdentifier{
|
||||
"abap": ABAPLanguage,
|
||||
"bat": BatLanguage,
|
||||
"bibtex": BibtexLanguage,
|
||||
"clojure": ClojureLanguage,
|
||||
"coffeescript": CoffeeScriptLanguage,
|
||||
"c": CLanguage,
|
||||
"cpp": CppLanguage,
|
||||
"csharp": CsharpLanguage,
|
||||
"css": CSSLanguage,
|
||||
"diff": DiffLanguage,
|
||||
"dart": DartLanguage,
|
||||
"dockerfile": DockerfileLanguage,
|
||||
"elixir": ElixirLanguage,
|
||||
"erlang": ErlangLanguage,
|
||||
"fsharp": FsharpLanguage,
|
||||
"git-commit": GitCommitLanguage,
|
||||
"git-rebase": GitRebaseLanguage,
|
||||
"go": GoLanguage,
|
||||
"groovy": GroovyLanguage,
|
||||
"handlebars": HandlebarsLanguage,
|
||||
"html": HTMLLanguage,
|
||||
"ini": IniLanguage,
|
||||
"java": JavaLanguage,
|
||||
"javascript": JavaScriptLanguage,
|
||||
"javascriptreact": JavaScriptReactLanguage,
|
||||
"json": JSONLanguage,
|
||||
"latex": LatexLanguage,
|
||||
"less": LessLanguage,
|
||||
"lua": LuaLanguage,
|
||||
"makefile": MakefileLanguage,
|
||||
"markdown": MarkdownLanguage,
|
||||
"objective-c": ObjectiveCLanguage,
|
||||
"objective-cpp": ObjectiveCppLanguage,
|
||||
"perl": PerlLanguage,
|
||||
"perl6": Perl6Language,
|
||||
"php": PHPLanguage,
|
||||
"powershell": PowershellLanguage,
|
||||
"jade": JadeLanguage,
|
||||
"python": PythonLanguage,
|
||||
"r": RLanguage,
|
||||
"razor": RazorLanguage,
|
||||
"ruby": RubyLanguage,
|
||||
"rust": RustLanguage,
|
||||
"scss": SCSSLanguage,
|
||||
"sass": SASSLanguage,
|
||||
"scala": ScalaLanguage,
|
||||
"shaderlab": ShaderlabLanguage,
|
||||
"shellscript": ShellscriptLanguage,
|
||||
"sql": SQLLanguage,
|
||||
"swift": SwiftLanguage,
|
||||
"typescript": TypeScriptLanguage,
|
||||
"typescriptreact": TypeScriptReactLanguage,
|
||||
"tex": TeXLanguage,
|
||||
"vb": VBLanguage,
|
||||
"xml": XMLLanguage,
|
||||
"xsl": XslLanguage,
|
||||
"yaml": YamlLanguage,
|
||||
}
|
||||
|
||||
// ToLanguageIdentifier converts ft to LanguageIdentifier.
|
||||
func ToLanguageIdentifier(ft string) LanguageIdentifier {
|
||||
langID, ok := languageIdentifierMap[ft]
|
||||
if ok {
|
||||
return langID
|
||||
}
|
||||
|
||||
return LanguageIdentifier(ft)
|
||||
}
|
||||
|
||||
// VersionedTextDocumentIdentifier represents an identifier to denote a specific version of a text document.
|
||||
//
|
||||
// This information usually flows from the client to the server.
|
||||
type VersionedTextDocumentIdentifier struct {
|
||||
TextDocumentIdentifier
|
||||
|
||||
// Version is the version number of this document.
|
||||
//
|
||||
// The version number of a document will increase after each change, including
|
||||
// undo/redo. The number doesn't need to be consecutive.
|
||||
Version int32 `json:"version"`
|
||||
}
|
||||
|
||||
// OptionalVersionedTextDocumentIdentifier represents an identifier which optionally denotes a specific version of
|
||||
// a text document.
|
||||
//
|
||||
// This information usually flows from the server to the client.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type OptionalVersionedTextDocumentIdentifier struct {
|
||||
TextDocumentIdentifier
|
||||
|
||||
// Version is the version number of this document. If an optional versioned text document
|
||||
// identifier is sent from the server to the client and the file is not
|
||||
// open in the editor (the server has not received an open notification
|
||||
// before) the server can send `null` to indicate that the version is
|
||||
// known and the content on disk is the master (as specified with document
|
||||
// content ownership).
|
||||
//
|
||||
// The version number of a document will increase after each change,
|
||||
// including undo/redo. The number doesn't need to be consecutive.
|
||||
Version *int32 `json:"version"` // int32 | null
|
||||
}
|
||||
|
||||
// TextDocumentPositionParams is a parameter literal used in requests to pass a text document and a position
|
||||
// inside that document.
|
||||
//
|
||||
// It is up to the client to decide how a selection is converted into a position when issuing a request for a text
|
||||
// document.
|
||||
//
|
||||
// The client can for example honor or ignore the selection direction to make LSP request consistent with features
|
||||
// implemented internally.
|
||||
type TextDocumentPositionParams struct {
|
||||
// TextDocument is the text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// Position is the position inside the text document.
|
||||
Position Position `json:"position"`
|
||||
}
|
||||
|
||||
// DocumentFilter is a document filter denotes a document through properties like language, scheme or pattern.
|
||||
//
|
||||
// An example is a filter that applies to TypeScript files on disk.
|
||||
type DocumentFilter struct {
|
||||
// Language a language id, like `typescript`.
|
||||
Language string `json:"language,omitempty"`
|
||||
|
||||
// Scheme a URI scheme, like `file` or `untitled`.
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
|
||||
// Pattern a glob pattern, like `*.{ts,js}`.
|
||||
//
|
||||
// Glob patterns can have the following syntax:
|
||||
// "*"
|
||||
// "*" to match one or more characters in a path segment
|
||||
// "?"
|
||||
// "?" to match on one character in a path segment
|
||||
// "**"
|
||||
// "**" to match any number of path segments, including none
|
||||
// "{}"
|
||||
// "{}" to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
|
||||
// "[]"
|
||||
// "[]" to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
|
||||
// "[!...]"
|
||||
// "[!...]" to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
}
|
||||
|
||||
// DocumentSelector is a document selector is the combination of one or more document filters.
|
||||
type DocumentSelector []*DocumentFilter
|
||||
|
||||
// MarkupKind describes the content type that a client supports in various
|
||||
// result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
|
||||
//
|
||||
// Please note that `MarkupKinds` must not start with a `$`. This kinds
|
||||
// are reserved for internal usage.
|
||||
type MarkupKind string
|
||||
|
||||
const (
|
||||
// PlainText is supported as a content format.
|
||||
PlainText MarkupKind = "plaintext"
|
||||
|
||||
// Markdown is supported as a content format.
|
||||
Markdown MarkupKind = "markdown"
|
||||
)
|
||||
|
||||
// MarkupContent a `MarkupContent` literal represents a string value which content is interpreted base on its
|
||||
// kind flag.
|
||||
//
|
||||
// Currently the protocol supports `plaintext` and `markdown` as markup kinds.
|
||||
//
|
||||
// If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues.
|
||||
// See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
|
||||
//
|
||||
// Here is an example how such a string can be constructed using JavaScript / TypeScript:
|
||||
//
|
||||
// let markdown: MarkdownContent = {
|
||||
// kind: MarkupKind.Markdown,
|
||||
// value: [
|
||||
// '# Header',
|
||||
// 'Some text',
|
||||
// '```typescript',
|
||||
// 'someCode();',
|
||||
// '```'
|
||||
// ].join('\n')
|
||||
// };
|
||||
//
|
||||
// NOTE: clients might sanitize the return markdown. A client could decide to
|
||||
// remove HTML from the markdown to avoid script execution.
|
||||
type MarkupContent struct {
|
||||
// Kind is the type of the Markup
|
||||
Kind MarkupKind `json:"kind"`
|
||||
|
||||
// Value is the content itself
|
||||
Value string `json:"value"`
|
||||
}
|
3213
templ/lsp/protocol/basic_test.go
Normal file
3213
templ/lsp/protocol/basic_test.go
Normal file
File diff suppressed because it is too large
Load Diff
103
templ/lsp/protocol/callhierarchy.go
Normal file
103
templ/lsp/protocol/callhierarchy.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// CallHierarchy capabilities specific to the "textDocument/callHierarchy".
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchy struct {
|
||||
// DynamicRegistration whether implementation supports dynamic registration.
|
||||
//
|
||||
// If this is set to "true" the client supports the new
|
||||
// TextDocumentRegistrationOptions && StaticRegistrationOptions return
|
||||
// value for the corresponding server capability as well.
|
||||
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
|
||||
}
|
||||
|
||||
// CallHierarchyPrepareParams params of CallHierarchyPrepare.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyPrepareParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
}
|
||||
|
||||
// CallHierarchyItem is the result of a "textDocument/prepareCallHierarchy" request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyItem struct {
|
||||
// name is the name of this item.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Kind is the kind of this item.
|
||||
Kind SymbolKind `json:"kind"`
|
||||
|
||||
// Tags for this item.
|
||||
Tags []SymbolTag `json:"tags,omitempty"`
|
||||
|
||||
// Detail more detail for this item, e.g. the signature of a function.
|
||||
Detail string `json:"detail,omitempty"`
|
||||
|
||||
// URI is the resource identifier of this item.
|
||||
URI DocumentURI `json:"uri"`
|
||||
|
||||
// Range is the range enclosing this symbol not including leading/trailing whitespace
|
||||
// but everything else, e.g. comments and code.
|
||||
Range Range `json:"range"`
|
||||
|
||||
// SelectionRange is the range that should be selected and revealed when this symbol is being
|
||||
// picked, e.g. the name of a function. Must be contained by the
|
||||
// Range.
|
||||
SelectionRange Range `json:"selectionRange"`
|
||||
|
||||
// Data is a data entry field that is preserved between a call hierarchy prepare and
|
||||
// incoming calls or outgoing calls requests.
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// CallHierarchyIncomingCallsParams params of CallHierarchyIncomingCalls.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyIncomingCallsParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// Item is the IncomingCalls item.
|
||||
Item CallHierarchyItem `json:"item"`
|
||||
}
|
||||
|
||||
// CallHierarchyIncomingCall is the result of a "callHierarchy/incomingCalls" request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyIncomingCall struct {
|
||||
// From is the item that makes the call.
|
||||
From CallHierarchyItem `json:"from"`
|
||||
|
||||
// FromRanges is the ranges at which the calls appear. This is relative to the caller
|
||||
// denoted by From.
|
||||
FromRanges []Range `json:"fromRanges"`
|
||||
}
|
||||
|
||||
// CallHierarchyOutgoingCallsParams params of CallHierarchyOutgoingCalls.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyOutgoingCallsParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// Item is the OutgoingCalls item.
|
||||
Item CallHierarchyItem `json:"item"`
|
||||
}
|
||||
|
||||
// CallHierarchyOutgoingCall is the result of a "callHierarchy/outgoingCalls" request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyOutgoingCall struct {
|
||||
// To is the item that is called.
|
||||
To CallHierarchyItem `json:"to"`
|
||||
|
||||
// FromRanges is the range at which this item is called. This is the range relative to
|
||||
// the caller, e.g the item passed to "callHierarchy/outgoingCalls" request.
|
||||
FromRanges []Range `json:"fromRanges"`
|
||||
}
|
1331
templ/lsp/protocol/callhierarchy_test.go
Normal file
1331
templ/lsp/protocol/callhierarchy_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1068
templ/lsp/protocol/capabilities_client.go
Normal file
1068
templ/lsp/protocol/capabilities_client.go
Normal file
File diff suppressed because it is too large
Load Diff
2787
templ/lsp/protocol/capabilities_client_test.go
Normal file
2787
templ/lsp/protocol/capabilities_client_test.go
Normal file
File diff suppressed because it is too large
Load Diff
523
templ/lsp/protocol/capabilities_server.go
Normal file
523
templ/lsp/protocol/capabilities_server.go
Normal file
@@ -0,0 +1,523 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ServerCapabilities efines the capabilities provided by a language server.
|
||||
type ServerCapabilities struct {
|
||||
// TextDocumentSync defines how text documents are synced. Is either a detailed structure defining each notification
|
||||
// or for backwards compatibility the TextDocumentSyncKind number.
|
||||
//
|
||||
// If omitted it defaults to TextDocumentSyncKind.None`
|
||||
TextDocumentSync any `json:"textDocumentSync,omitempty"` // *TextDocumentSyncOptions | TextDocumentSyncKind
|
||||
|
||||
// CompletionProvider is The server provides completion support.
|
||||
CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"`
|
||||
|
||||
// HoverProvider is the server provides hover support.
|
||||
HoverProvider any `json:"hoverProvider,omitempty"` // TODO(zchee): bool | *HoverOptions
|
||||
|
||||
// SignatureHelpProvider is the server provides signature help support.
|
||||
SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
|
||||
|
||||
// DeclarationProvider is the server provides Goto Declaration support.
|
||||
//
|
||||
// @since 3.14.0.
|
||||
DeclarationProvider any `json:"declarationProvider,omitempty"` // TODO(zchee): bool | *DeclarationOptions | *DeclarationRegistrationOptions
|
||||
|
||||
// DefinitionProvider is the server provides Goto definition support.
|
||||
DefinitionProvider any `json:"definitionProvider,omitempty"` // TODO(zchee): bool | *DefinitionOptions
|
||||
|
||||
// TypeDefinitionProvider is the provides Goto Type Definition support.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
TypeDefinitionProvider any `json:"typeDefinitionProvider,omitempty"` // TODO(zchee): bool | *TypeDefinitionOptions | *TypeDefinitionRegistrationOptions
|
||||
|
||||
// ImplementationProvider is the provides Goto Implementation support.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
ImplementationProvider any `json:"implementationProvider,omitempty"` // TODO(zchee): bool | *ImplementationOptions | *ImplementationRegistrationOptions
|
||||
|
||||
// ReferencesProvider is the server provides find references support.
|
||||
ReferencesProvider any `json:"referencesProvider,omitempty"` // TODO(zchee): bool | *ReferenceOptions
|
||||
|
||||
// DocumentHighlightProvider is the server provides document highlight support.
|
||||
DocumentHighlightProvider any `json:"documentHighlightProvider,omitempty"` // TODO(zchee): bool | *DocumentHighlightOptions
|
||||
|
||||
// DocumentSymbolProvider is the server provides document symbol support.
|
||||
DocumentSymbolProvider any `json:"documentSymbolProvider,omitempty"` // TODO(zchee): bool | *DocumentSymbolOptions
|
||||
|
||||
// CodeActionProvider is the server provides code actions.
|
||||
//
|
||||
// CodeActionOptions may only be specified if the client states that it supports CodeActionLiteralSupport in its
|
||||
// initial Initialize request.
|
||||
CodeActionProvider any `json:"codeActionProvider,omitempty"` // TODO(zchee): bool | *CodeActionOptions
|
||||
|
||||
// CodeLensProvider is the server provides code lens.
|
||||
CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"`
|
||||
|
||||
// The server provides document link support.
|
||||
DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"`
|
||||
|
||||
// ColorProvider is the server provides color provider support.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
ColorProvider any `json:"colorProvider,omitempty"` // TODO(zchee): bool | *DocumentColorOptions | *DocumentColorRegistrationOptions
|
||||
|
||||
// WorkspaceSymbolProvider is the server provides workspace symbol support.
|
||||
WorkspaceSymbolProvider any `json:"workspaceSymbolProvider,omitempty"` // TODO(zchee): bool | *WorkspaceSymbolOptions
|
||||
|
||||
// DocumentFormattingProvider is the server provides document formatting.
|
||||
DocumentFormattingProvider any `json:"documentFormattingProvider,omitempty"` // TODO(zchee): bool | *DocumentFormattingOptions
|
||||
|
||||
// DocumentRangeFormattingProvider is the server provides document range formatting.
|
||||
DocumentRangeFormattingProvider any `json:"documentRangeFormattingProvider,omitempty"` // TODO(zchee): bool | *DocumentRangeFormattingOptions
|
||||
|
||||
// DocumentOnTypeFormattingProvider is the server provides document formatting on typing.
|
||||
DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
|
||||
|
||||
// RenameProvider is the server provides rename support.
|
||||
//
|
||||
// RenameOptions may only be specified if the client states that it supports PrepareSupport in its
|
||||
// initial Initialize request.
|
||||
RenameProvider any `json:"renameProvider,omitempty"` // TODO(zchee): bool | *RenameOptions
|
||||
|
||||
// FoldingRangeProvider is the server provides folding provider support.
|
||||
//
|
||||
// @since 3.10.0.
|
||||
FoldingRangeProvider any `json:"foldingRangeProvider,omitempty"` // TODO(zchee): bool | *FoldingRangeOptions | *FoldingRangeRegistrationOptions
|
||||
|
||||
// SelectionRangeProvider is the server provides selection range support.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
SelectionRangeProvider any `json:"selectionRangeProvider,omitempty"` // TODO(zchee): bool | *SelectionRangeOptions | *SelectionRangeRegistrationOptions
|
||||
|
||||
// ExecuteCommandProvider is the server provides execute command support.
|
||||
ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
|
||||
|
||||
// CallHierarchyProvider is the server provides call hierarchy support.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
CallHierarchyProvider any `json:"callHierarchyProvider,omitempty"` // TODO(zchee): bool | *CallHierarchyOptions | *CallHierarchyRegistrationOptions
|
||||
|
||||
// LinkedEditingRangeProvider is the server provides linked editing range support.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
LinkedEditingRangeProvider any `json:"linkedEditingRangeProvider,omitempty"` // TODO(zchee): bool | *LinkedEditingRangeOptions | *LinkedEditingRangeRegistrationOptions
|
||||
|
||||
// SemanticTokensProvider is the server provides semantic tokens support.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
SemanticTokensProvider any `json:"semanticTokensProvider,omitempty"` // TODO(zchee): *SemanticTokensOptions | *SemanticTokensRegistrationOptions
|
||||
|
||||
// Workspace is the window specific server capabilities.
|
||||
Workspace *ServerCapabilitiesWorkspace `json:"workspace,omitempty"`
|
||||
|
||||
// MonikerProvider is the server provides moniker support.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
MonikerProvider any `json:"monikerProvider,omitempty"` // TODO(zchee): bool | *MonikerOptions | *MonikerRegistrationOptions
|
||||
|
||||
// Experimental server capabilities.
|
||||
Experimental any `json:"experimental,omitempty"`
|
||||
}
|
||||
|
||||
// TextDocumentSyncOptions TextDocumentSync options.
|
||||
type TextDocumentSyncOptions struct {
|
||||
// OpenClose open and close notifications are sent to the server.
|
||||
OpenClose bool `json:"openClose,omitempty"`
|
||||
|
||||
// Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full
|
||||
// and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None.
|
||||
Change TextDocumentSyncKind `json:"change,omitempty"`
|
||||
|
||||
// WillSave notifications are sent to the server.
|
||||
WillSave bool `json:"willSave,omitempty"`
|
||||
|
||||
// WillSaveWaitUntil will save wait until requests are sent to the server.
|
||||
WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"`
|
||||
|
||||
// Save notifications are sent to the server.
|
||||
Save *SaveOptions `json:"save,omitempty"`
|
||||
}
|
||||
|
||||
// SaveOptions save options.
|
||||
type SaveOptions struct {
|
||||
// IncludeText is the client is supposed to include the content on save.
|
||||
IncludeText bool `json:"includeText,omitempty"`
|
||||
}
|
||||
|
||||
// TextDocumentSyncKind defines how the host (editor) should sync document changes to the language server.
|
||||
type TextDocumentSyncKind float64
|
||||
|
||||
const (
|
||||
// TextDocumentSyncKindNone documents should not be synced at all.
|
||||
TextDocumentSyncKindNone TextDocumentSyncKind = 0
|
||||
|
||||
// TextDocumentSyncKindFull documents are synced by always sending the full content
|
||||
// of the document.
|
||||
TextDocumentSyncKindFull TextDocumentSyncKind = 1
|
||||
|
||||
// TextDocumentSyncKindIncremental documents are synced by sending the full content on open.
|
||||
// After that only incremental updates to the document are
|
||||
// send.
|
||||
TextDocumentSyncKindIncremental TextDocumentSyncKind = 2
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (k TextDocumentSyncKind) String() string {
|
||||
switch k {
|
||||
case TextDocumentSyncKindNone:
|
||||
return "None"
|
||||
case TextDocumentSyncKindFull:
|
||||
return "Full"
|
||||
case TextDocumentSyncKindIncremental:
|
||||
return "Incremental"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(k), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// CompletionOptions Completion options.
|
||||
type CompletionOptions struct {
|
||||
// The server provides support to resolve additional
|
||||
// information for a completion item.
|
||||
ResolveProvider bool `json:"resolveProvider,omitempty"`
|
||||
|
||||
// The characters that trigger completion automatically.
|
||||
TriggerCharacters []string `json:"triggerCharacters,omitempty"`
|
||||
}
|
||||
|
||||
// HoverOptions option of hover provider server capabilities.
|
||||
type HoverOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// SignatureHelpOptions SignatureHelp options.
|
||||
type SignatureHelpOptions struct {
|
||||
// The characters that trigger signature help
|
||||
// automatically.
|
||||
TriggerCharacters []string `json:"triggerCharacters,omitempty"`
|
||||
|
||||
// RetriggerCharacters is the slist of characters that re-trigger signature help.
|
||||
//
|
||||
// These trigger characters are only active when signature help is already
|
||||
// showing.
|
||||
// All trigger characters are also counted as re-trigger characters.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
RetriggerCharacters []string `json:"retriggerCharacters,omitempty"`
|
||||
}
|
||||
|
||||
// DeclarationOptions registration option of Declaration server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DeclarationOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DeclarationRegistrationOptions registration option of Declaration server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DeclarationRegistrationOptions struct {
|
||||
DeclarationOptions
|
||||
TextDocumentRegistrationOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// DefinitionOptions registration option of Definition server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DefinitionOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// TypeDefinitionOptions registration option of TypeDefinition server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type TypeDefinitionOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// TypeDefinitionRegistrationOptions registration option of TypeDefinition server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type TypeDefinitionRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
TypeDefinitionOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// ImplementationOptions registration option of Implementation server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ImplementationOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// ImplementationRegistrationOptions registration option of Implementation server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ImplementationRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
ImplementationOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// ReferenceOptions registration option of Reference server capability.
|
||||
type ReferenceOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentHighlightOptions registration option of DocumentHighlight server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentHighlightOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentSymbolOptions registration option of DocumentSymbol server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentSymbolOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
|
||||
// Label a human-readable string that is shown when multiple outlines trees
|
||||
// are shown for the same document.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
Label string `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
// CodeActionOptions CodeAction options.
|
||||
type CodeActionOptions struct {
|
||||
// CodeActionKinds that this server may return.
|
||||
//
|
||||
// The list of kinds may be generic, such as "CodeActionKind.Refactor", or the server
|
||||
// may list out every specific kind they provide.
|
||||
CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"`
|
||||
|
||||
// ResolveProvider is the server provides support to resolve additional
|
||||
// information for a code action.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
ResolveProvider bool `json:"resolveProvider,omitempty"`
|
||||
}
|
||||
|
||||
// CodeLensOptions CodeLens options.
|
||||
type CodeLensOptions struct {
|
||||
// Code lens has a resolve provider as well.
|
||||
ResolveProvider bool `json:"resolveProvider,omitempty"`
|
||||
}
|
||||
|
||||
// DocumentLinkOptions document link options.
|
||||
type DocumentLinkOptions struct {
|
||||
// ResolveProvider document links have a resolve provider as well.
|
||||
ResolveProvider bool `json:"resolveProvider,omitempty"`
|
||||
}
|
||||
|
||||
// DocumentColorOptions registration option of DocumentColor server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentColorOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentColorRegistrationOptions registration option of DocumentColor server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentColorRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
StaticRegistrationOptions
|
||||
DocumentColorOptions
|
||||
}
|
||||
|
||||
// WorkspaceSymbolOptions registration option of WorkspaceSymbol server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkspaceSymbolOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentFormattingOptions registration option of DocumentFormatting server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentFormattingOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentRangeFormattingOptions registration option of DocumentRangeFormatting server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentRangeFormattingOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// DocumentOnTypeFormattingOptions format document on type options.
|
||||
type DocumentOnTypeFormattingOptions struct {
|
||||
// FirstTriggerCharacter a character on which formatting should be triggered, like "}".
|
||||
FirstTriggerCharacter string `json:"firstTriggerCharacter"`
|
||||
|
||||
// MoreTriggerCharacter more trigger characters.
|
||||
MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"`
|
||||
}
|
||||
|
||||
// RenameOptions rename options.
|
||||
type RenameOptions struct {
|
||||
// PrepareProvider renames should be checked and tested before being executed.
|
||||
PrepareProvider bool `json:"prepareProvider,omitempty"`
|
||||
}
|
||||
|
||||
// FoldingRangeOptions registration option of FoldingRange server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type FoldingRangeOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// FoldingRangeRegistrationOptions registration option of FoldingRange server capability.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type FoldingRangeRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
FoldingRangeOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// ExecuteCommandOptions execute command options.
|
||||
type ExecuteCommandOptions struct {
|
||||
// Commands is the commands to be executed on the server
|
||||
Commands []string `json:"commands"`
|
||||
}
|
||||
|
||||
// CallHierarchyOptions option of CallHierarchy.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// CallHierarchyRegistrationOptions registration options of CallHierarchy.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CallHierarchyRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
CallHierarchyOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// LinkedEditingRangeOptions option of linked editing range provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type LinkedEditingRangeOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// LinkedEditingRangeRegistrationOptions registration option of linked editing range provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type LinkedEditingRangeRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
LinkedEditingRangeOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// SemanticTokensOptions option of semantic tokens provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// SemanticTokensRegistrationOptions registration option of semantic tokens provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
SemanticTokensOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// ServerCapabilitiesWorkspace specific server capabilities.
|
||||
type ServerCapabilitiesWorkspace struct {
|
||||
// WorkspaceFolders is the server supports workspace folder.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
WorkspaceFolders *ServerCapabilitiesWorkspaceFolders `json:"workspaceFolders,omitempty"`
|
||||
|
||||
// FileOperations is the server is interested in file notifications/requests.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
FileOperations *ServerCapabilitiesWorkspaceFileOperations `json:"fileOperations,omitempty"`
|
||||
}
|
||||
|
||||
// ServerCapabilitiesWorkspaceFolders is the server supports workspace folder.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
type ServerCapabilitiesWorkspaceFolders struct {
|
||||
// Supported is the server has support for workspace folders
|
||||
Supported bool `json:"supported,omitempty"`
|
||||
|
||||
// ChangeNotifications whether the server wants to receive workspace folder
|
||||
// change notifications.
|
||||
//
|
||||
// If a strings is provided the string is treated as a ID
|
||||
// under which the notification is registered on the client
|
||||
// side. The ID can be used to unregister for these events
|
||||
// using the `client/unregisterCapability` request.
|
||||
ChangeNotifications any `json:"changeNotifications,omitempty"` // string | boolean
|
||||
}
|
||||
|
||||
// ServerCapabilitiesWorkspaceFileOperations is the server is interested in file notifications/requests.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type ServerCapabilitiesWorkspaceFileOperations struct {
|
||||
// DidCreate is the server is interested in receiving didCreateFiles
|
||||
// notifications.
|
||||
DidCreate *FileOperationRegistrationOptions `json:"didCreate,omitempty"`
|
||||
|
||||
// WillCreate is the server is interested in receiving willCreateFiles requests.
|
||||
WillCreate *FileOperationRegistrationOptions `json:"willCreate,omitempty"`
|
||||
|
||||
// DidRename is the server is interested in receiving didRenameFiles
|
||||
// notifications.
|
||||
DidRename *FileOperationRegistrationOptions `json:"didRename,omitempty"`
|
||||
|
||||
// WillRename is the server is interested in receiving willRenameFiles requests.
|
||||
WillRename *FileOperationRegistrationOptions `json:"willRename,omitempty"`
|
||||
|
||||
// DidDelete is the server is interested in receiving didDeleteFiles file
|
||||
// notifications.
|
||||
DidDelete *FileOperationRegistrationOptions `json:"didDelete,omitempty"`
|
||||
|
||||
// WillDelete is the server is interested in receiving willDeleteFiles file
|
||||
// requests.
|
||||
WillDelete *FileOperationRegistrationOptions `json:"willDelete,omitempty"`
|
||||
}
|
||||
|
||||
// FileOperationRegistrationOptions is the options to register for file operations.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileOperationRegistrationOptions struct {
|
||||
// filters is the actual filters.
|
||||
Filters []FileOperationFilter `json:"filters"`
|
||||
}
|
||||
|
||||
// MonikerOptions option of moniker provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type MonikerOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// MonikerRegistrationOptions registration option of moniker provider server capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type MonikerRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
MonikerOptions
|
||||
}
|
412
templ/lsp/protocol/client.go
Normal file
412
templ/lsp/protocol/client.go
Normal file
@@ -0,0 +1,412 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
"github.com/a-h/templ/lsp/xcontext"
|
||||
)
|
||||
|
||||
// ClientDispatcher returns a Client that dispatches LSP requests across the
|
||||
// given jsonrpc2 connection.
|
||||
func ClientDispatcher(conn jsonrpc2.Conn, logger *slog.Logger) Client {
|
||||
return &client{
|
||||
Conn: conn,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientHandler handler of LSP client.
|
||||
func ClientHandler(log *slog.Logger, client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
h := func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
if ctx.Err() != nil {
|
||||
xctx := xcontext.Detach(ctx)
|
||||
|
||||
return reply(xctx, nil, ErrRequestCancelled)
|
||||
}
|
||||
|
||||
handled, err := clientDispatch(ctx, log, client, reply, req)
|
||||
if handled || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return handler(ctx, reply, req)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// clientDispatch implements jsonrpc2.Handler.
|
||||
//
|
||||
//nolint:funlen,cyclop
|
||||
func clientDispatch(ctx context.Context, log *slog.Logger, client Client, reply jsonrpc2.Replier, req jsonrpc2.Request) (handled bool, err error) {
|
||||
if ctx.Err() != nil {
|
||||
return true, reply(ctx, nil, ErrRequestCancelled)
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(req.Params()))
|
||||
|
||||
switch req.Method() {
|
||||
case MethodProgress: // notification
|
||||
defer log.Debug(MethodProgress, slog.Any("error", err))
|
||||
|
||||
var params ProgressParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.Progress(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodWorkDoneProgressCreate: // request
|
||||
defer log.Debug(MethodWorkDoneProgressCreate, slog.Any("error", err))
|
||||
|
||||
var params WorkDoneProgressCreateParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.WorkDoneProgressCreate(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodWindowLogMessage: // notification
|
||||
defer log.Debug(MethodWindowLogMessage, slog.Any("error", err))
|
||||
|
||||
var params LogMessageParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.LogMessage(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodTextDocumentPublishDiagnostics: // notification
|
||||
defer log.Debug(MethodTextDocumentPublishDiagnostics, slog.Any("error", err))
|
||||
|
||||
var params PublishDiagnosticsParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.PublishDiagnostics(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodWindowShowMessage: // notification
|
||||
defer log.Debug(MethodWindowShowMessage, slog.Any("error", err))
|
||||
|
||||
var params ShowMessageParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.ShowMessage(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodWindowShowMessageRequest: // request
|
||||
defer log.Debug(MethodWindowShowMessageRequest, slog.Any("error", err))
|
||||
|
||||
var params ShowMessageRequestParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
resp, err := client.ShowMessageRequest(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, resp, err)
|
||||
|
||||
case MethodTelemetryEvent: // notification
|
||||
defer log.Debug(MethodTelemetryEvent, slog.Any("error", err))
|
||||
|
||||
var params any
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.Telemetry(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodClientRegisterCapability: // request
|
||||
defer log.Debug(MethodClientRegisterCapability, slog.Any("error", err))
|
||||
|
||||
var params RegistrationParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.RegisterCapability(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodClientUnregisterCapability: // request
|
||||
defer log.Debug(MethodClientUnregisterCapability, slog.Any("error", err))
|
||||
|
||||
var params UnregistrationParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
err := client.UnregisterCapability(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, nil, err)
|
||||
|
||||
case MethodWorkspaceApplyEdit: // request
|
||||
defer log.Debug(MethodWorkspaceApplyEdit, slog.Any("error", err))
|
||||
|
||||
var params ApplyWorkspaceEditParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
resp, err := client.ApplyEdit(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, resp, err)
|
||||
|
||||
case MethodWorkspaceConfiguration: // request
|
||||
defer log.Debug(MethodWorkspaceConfiguration, slog.Any("error", err))
|
||||
|
||||
var params ConfigurationParams
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
return true, replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
resp, err := client.Configuration(ctx, ¶ms)
|
||||
|
||||
return true, reply(ctx, resp, err)
|
||||
|
||||
case MethodWorkspaceWorkspaceFolders: // request
|
||||
defer log.Debug(MethodWorkspaceWorkspaceFolders, slog.Any("error", err))
|
||||
|
||||
if len(req.Params()) > 0 {
|
||||
return true, reply(ctx, nil, fmt.Errorf("expected no params: %w", jsonrpc2.ErrInvalidParams))
|
||||
}
|
||||
|
||||
resp, err := client.WorkspaceFolders(ctx)
|
||||
|
||||
return true, reply(ctx, resp, err)
|
||||
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Client represents a Language Server Protocol client.
|
||||
type Client interface {
|
||||
Progress(ctx context.Context, params *ProgressParams) (err error)
|
||||
WorkDoneProgressCreate(ctx context.Context, params *WorkDoneProgressCreateParams) (err error)
|
||||
LogMessage(ctx context.Context, params *LogMessageParams) (err error)
|
||||
PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) (err error)
|
||||
ShowMessage(ctx context.Context, params *ShowMessageParams) (err error)
|
||||
ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (result *MessageActionItem, err error)
|
||||
Telemetry(ctx context.Context, params any) (err error)
|
||||
RegisterCapability(ctx context.Context, params *RegistrationParams) (err error)
|
||||
UnregisterCapability(ctx context.Context, params *UnregistrationParams) (err error)
|
||||
ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (result *ApplyWorkspaceEditResponse, err error)
|
||||
Configuration(ctx context.Context, params *ConfigurationParams) (result []any, err error)
|
||||
WorkspaceFolders(ctx context.Context) (result []WorkspaceFolder, err error)
|
||||
}
|
||||
|
||||
// list of client methods.
|
||||
const (
|
||||
// MethodProgress method name of "$/progress".
|
||||
MethodProgress = "$/progress"
|
||||
|
||||
// MethodWorkDoneProgressCreate method name of "window/workDoneProgress/create".
|
||||
MethodWorkDoneProgressCreate = "window/workDoneProgress/create"
|
||||
|
||||
// MethodWindowShowMessage method name of "window/showMessage".
|
||||
MethodWindowShowMessage = "window/showMessage"
|
||||
|
||||
// MethodWindowShowMessageRequest method name of "window/showMessageRequest.
|
||||
MethodWindowShowMessageRequest = "window/showMessageRequest"
|
||||
|
||||
// MethodWindowLogMessage method name of "window/logMessage.
|
||||
MethodWindowLogMessage = "window/logMessage"
|
||||
|
||||
// MethodTelemetryEvent method name of "telemetry/event.
|
||||
MethodTelemetryEvent = "telemetry/event"
|
||||
|
||||
// MethodClientRegisterCapability method name of "client/registerCapability.
|
||||
MethodClientRegisterCapability = "client/registerCapability"
|
||||
|
||||
// MethodClientUnregisterCapability method name of "client/unregisterCapability.
|
||||
MethodClientUnregisterCapability = "client/unregisterCapability"
|
||||
|
||||
// MethodTextDocumentPublishDiagnostics method name of "textDocument/publishDiagnostics.
|
||||
MethodTextDocumentPublishDiagnostics = "textDocument/publishDiagnostics"
|
||||
|
||||
// MethodWorkspaceApplyEdit method name of "workspace/applyEdit.
|
||||
MethodWorkspaceApplyEdit = "workspace/applyEdit"
|
||||
|
||||
// MethodWorkspaceConfiguration method name of "workspace/configuration.
|
||||
MethodWorkspaceConfiguration = "workspace/configuration"
|
||||
|
||||
// MethodWorkspaceWorkspaceFolders method name of "workspace/workspaceFolders".
|
||||
MethodWorkspaceWorkspaceFolders = "workspace/workspaceFolders"
|
||||
)
|
||||
|
||||
// client implements a Language Server Protocol client.
|
||||
type client struct {
|
||||
jsonrpc2.Conn
|
||||
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// compiler time check whether the Client implements ClientInterface interface.
|
||||
var _ Client = (*client)(nil)
|
||||
|
||||
// Progress is the base protocol offers also support to report progress in a generic fashion.
|
||||
//
|
||||
// This mechanism can be used to report any kind of progress including work done progress (usually used to report progress in the user interface using a progress bar) and
|
||||
// partial result progress to support streaming of results.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
func (c *client) Progress(ctx context.Context, params *ProgressParams) (err error) {
|
||||
c.logger.Debug("call " + MethodProgress)
|
||||
defer c.logger.Debug("end "+MethodProgress, slog.Any("error", err))
|
||||
|
||||
return c.Conn.Notify(ctx, MethodProgress, params)
|
||||
}
|
||||
|
||||
// WorkDoneProgressCreate sends the request is sent from the server to the client to ask the client to create a work done progress.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
func (c *client) WorkDoneProgressCreate(ctx context.Context, params *WorkDoneProgressCreateParams) (err error) {
|
||||
c.logger.Debug("call " + MethodWorkDoneProgressCreate)
|
||||
defer c.logger.Debug("end "+MethodWorkDoneProgressCreate, slog.Any("error", err))
|
||||
|
||||
return Call(ctx, c.Conn, MethodWorkDoneProgressCreate, params, nil)
|
||||
}
|
||||
|
||||
// LogMessage sends the notification from the server to the client to ask the client to log a particular message.
|
||||
func (c *client) LogMessage(ctx context.Context, params *LogMessageParams) (err error) {
|
||||
c.logger.Debug("call " + MethodWindowLogMessage)
|
||||
defer c.logger.Debug("end "+MethodWindowLogMessage, slog.Any("error", err))
|
||||
|
||||
return c.Conn.Notify(ctx, MethodWindowLogMessage, params)
|
||||
}
|
||||
|
||||
// PublishDiagnostics sends the notification from the server to the client to signal results of validation runs.
|
||||
//
|
||||
// Diagnostics are “owned” by the server so it is the server’s responsibility to clear them if necessary. The following rule is used for VS Code servers that generate diagnostics:
|
||||
//
|
||||
// - if a language is single file only (for example HTML) then diagnostics are cleared by the server when the file is closed.
|
||||
// - if a language has a project system (for example C#) diagnostics are not cleared when a file closes. When a project is opened all diagnostics for all files are recomputed (or read from a cache).
|
||||
//
|
||||
// When a file changes it is the server’s responsibility to re-compute diagnostics and push them to the client.
|
||||
// If the computed set is empty it has to push the empty array to clear former diagnostics.
|
||||
// Newly pushed diagnostics always replace previously pushed diagnostics. There is no merging that happens on the client side.
|
||||
func (c *client) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) (err error) {
|
||||
c.logger.Debug("call " + MethodTextDocumentPublishDiagnostics)
|
||||
defer c.logger.Debug("end "+MethodTextDocumentPublishDiagnostics, slog.Any("error", err))
|
||||
|
||||
return c.Conn.Notify(ctx, MethodTextDocumentPublishDiagnostics, params)
|
||||
}
|
||||
|
||||
// ShowMessage sends the notification from a server to a client to ask the
|
||||
// client to display a particular message in the user interface.
|
||||
func (c *client) ShowMessage(ctx context.Context, params *ShowMessageParams) (err error) {
|
||||
return c.Conn.Notify(ctx, MethodWindowShowMessage, params)
|
||||
}
|
||||
|
||||
// ShowMessageRequest sends the request from a server to a client to ask the client to display a particular message in the user interface.
|
||||
//
|
||||
// In addition to the show message notification the request allows to pass actions and to wait for an answer from the client.
|
||||
func (c *client) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (_ *MessageActionItem, err error) {
|
||||
c.logger.Debug("call " + MethodWindowShowMessageRequest)
|
||||
defer c.logger.Debug("end "+MethodWindowShowMessageRequest, slog.Any("error", err))
|
||||
|
||||
var result *MessageActionItem
|
||||
if err := Call(ctx, c.Conn, MethodWindowShowMessageRequest, params, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Telemetry sends the notification from the server to the client to ask the client to log a telemetry event.
|
||||
func (c *client) Telemetry(ctx context.Context, params any) (err error) {
|
||||
c.logger.Debug("call " + MethodTelemetryEvent)
|
||||
defer c.logger.Debug("end "+MethodTelemetryEvent, slog.Any("error", err))
|
||||
|
||||
return c.Conn.Notify(ctx, MethodTelemetryEvent, params)
|
||||
}
|
||||
|
||||
// RegisterCapability sends the request from the server to the client to register for a new capability on the client side.
|
||||
//
|
||||
// Not all clients need to support dynamic capability registration.
|
||||
//
|
||||
// A client opts in via the dynamicRegistration property on the specific client capabilities.
|
||||
// A client can even provide dynamic registration for capability A but not for capability B (see TextDocumentClientCapabilities as an example).
|
||||
func (c *client) RegisterCapability(ctx context.Context, params *RegistrationParams) (err error) {
|
||||
c.logger.Debug("call " + MethodClientRegisterCapability)
|
||||
defer c.logger.Debug("end "+MethodClientRegisterCapability, slog.Any("error", err))
|
||||
|
||||
return Call(ctx, c.Conn, MethodClientRegisterCapability, params, nil)
|
||||
}
|
||||
|
||||
// UnregisterCapability sends the request from the server to the client to unregister a previously registered capability.
|
||||
func (c *client) UnregisterCapability(ctx context.Context, params *UnregistrationParams) (err error) {
|
||||
c.logger.Debug("call " + MethodClientUnregisterCapability)
|
||||
defer c.logger.Debug("end "+MethodClientUnregisterCapability, slog.Any("error", err))
|
||||
|
||||
return Call(ctx, c.Conn, MethodClientUnregisterCapability, params, nil)
|
||||
}
|
||||
|
||||
// ApplyEdit sends the request from the server to the client to modify resource on the client side.
|
||||
func (c *client) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (result *ApplyWorkspaceEditResponse, err error) {
|
||||
c.logger.Debug("call " + MethodWorkspaceApplyEdit)
|
||||
defer c.logger.Debug("end "+MethodWorkspaceApplyEdit, slog.Any("error", err))
|
||||
|
||||
if err := Call(ctx, c.Conn, MethodWorkspaceApplyEdit, params, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Configuration sends the request from the server to the client to fetch configuration settings from the client.
|
||||
//
|
||||
// The request can fetch several configuration settings in one roundtrip.
|
||||
// The order of the returned configuration settings correspond to the order of the
|
||||
// passed ConfigurationItems (e.g. the first item in the response is the result for the first configuration item in the params).
|
||||
func (c *client) Configuration(ctx context.Context, params *ConfigurationParams) (_ []any, err error) {
|
||||
c.logger.Debug("call " + MethodWorkspaceConfiguration)
|
||||
defer c.logger.Debug("end "+MethodWorkspaceConfiguration, slog.Any("error", err))
|
||||
|
||||
var result []any
|
||||
if err := Call(ctx, c.Conn, MethodWorkspaceConfiguration, params, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// WorkspaceFolders sends the request from the server to the client to fetch the current open list of workspace folders.
|
||||
//
|
||||
// Returns null in the response if only a single file is open in the tool. Returns an empty array if a workspace is open but no folders are configured.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
func (c *client) WorkspaceFolders(ctx context.Context) (result []WorkspaceFolder, err error) {
|
||||
c.logger.Debug("call " + MethodWorkspaceWorkspaceFolders)
|
||||
defer c.logger.Debug("end "+MethodWorkspaceWorkspaceFolders, slog.Any("error", err))
|
||||
|
||||
if err := Call(ctx, c.Conn, MethodWorkspaceWorkspaceFolders, nil, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
26
templ/lsp/protocol/context.go
Normal file
26
templ/lsp/protocol/context.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type ctxClientKey int
|
||||
|
||||
var ctxClient ctxClientKey = 0
|
||||
|
||||
// WithClient returns the context with Client value.
|
||||
func WithClient(ctx context.Context, client Client) context.Context {
|
||||
return context.WithValue(ctx, ctxClient, client)
|
||||
}
|
||||
|
||||
// ClientFromContext extracts Client from context.
|
||||
func ClientFromContext(ctx context.Context) Client {
|
||||
client, ok := ctx.Value(ctxClient).(Client)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return client
|
||||
}
|
264
templ/lsp/protocol/deprecated.go
Normal file
264
templ/lsp/protocol/deprecated.go
Normal file
@@ -0,0 +1,264 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// ClientCapabilitiesShowDocument alias of ShowDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use ShowDocumentClientCapabilities instead.
|
||||
type ClientCapabilitiesShowDocument = ShowDocumentClientCapabilities
|
||||
|
||||
// ClientCapabilitiesShowMessageRequest alias of ShowMessageRequestClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use ShowMessageRequestClientCapabilities instead.
|
||||
type ClientCapabilitiesShowMessageRequest = ShowMessageRequestClientCapabilities
|
||||
|
||||
// ClientCapabilitiesShowMessageRequestMessageActionItem alias of ShowMessageRequestClientCapabilitiesMessageActionItem.
|
||||
//
|
||||
// Deprecated: Use ShowMessageRequestClientCapabilitiesMessageActionItem instead.
|
||||
type ClientCapabilitiesShowMessageRequestMessageActionItem = ShowMessageRequestClientCapabilitiesMessageActionItem
|
||||
|
||||
// ReferencesParams alias of ReferenceParams.
|
||||
//
|
||||
// Deprecated: Use ReferenceParams instead.
|
||||
type ReferencesParams = ReferenceParams
|
||||
|
||||
// TextDocumentClientCapabilitiesCallHierarchy alias of CallHierarchyClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use CallHierarchyClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesCallHierarchy = CallHierarchyClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesCodeAction alias of CodeActionClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use CodeActionClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesCodeAction = CodeActionClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesCodeActionKind alias of CodeActionClientCapabilitiesKind.
|
||||
//
|
||||
// Deprecated: Use CodeActionClientCapabilitiesKind instead.
|
||||
type TextDocumentClientCapabilitiesCodeActionKind = CodeActionClientCapabilitiesKind
|
||||
|
||||
// TextDocumentClientCapabilitiesCodeActionLiteralSupport alias of CodeActionClientCapabilitiesLiteralSupport.
|
||||
//
|
||||
// Deprecated: Use CodeActionClientCapabilitiesLiteralSupport instead.
|
||||
type TextDocumentClientCapabilitiesCodeActionLiteralSupport = CodeActionClientCapabilitiesLiteralSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesCodeActionResolveSupport alias of CodeActionClientCapabilitiesResolveSupport.
|
||||
//
|
||||
// Deprecated: Use CodeActionClientCapabilitiesResolveSupport instead.
|
||||
type TextDocumentClientCapabilitiesCodeActionResolveSupport = CodeActionClientCapabilitiesResolveSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesCodeLens alias of CodeLensClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use CodeLensClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesCodeLens = CodeLensClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesColorProvider alias of DocumentColorClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentColorClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesColorProvider = DocumentColorClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletion alias of CompletionTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesCompletion = CompletionTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletionItem alias of CompletionTextDocumentClientCapabilitiesItem.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilitiesItem instead.
|
||||
type TextDocumentClientCapabilitiesCompletionItem = CompletionTextDocumentClientCapabilitiesItem
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletionItemInsertTextModeSupport alias of CompletionTextDocumentClientCapabilitiesItemInsertTextModeSupport.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilitiesItemInsertTextModeSupport instead.
|
||||
type TextDocumentClientCapabilitiesCompletionItemInsertTextModeSupport = CompletionTextDocumentClientCapabilitiesItemInsertTextModeSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletionItemKind alias of CompletionTextDocumentClientCapabilitiesItemKind.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilitiesItemKind instead.
|
||||
type TextDocumentClientCapabilitiesCompletionItemKind = CompletionTextDocumentClientCapabilitiesItemKind
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletionItemResolveSupport alias of CompletionTextDocumentClientCapabilitiesItemResolveSupport.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilitiesItemResolveSupport instead.
|
||||
type TextDocumentClientCapabilitiesCompletionItemResolveSupport = CompletionTextDocumentClientCapabilitiesItemResolveSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesCompletionItemTagSupport alias of CompletionTextDocumentClientCapabilitiesItemTagSupport.
|
||||
//
|
||||
// Deprecated: Use CompletionTextDocumentClientCapabilitiesItemTagSupport instead.
|
||||
type TextDocumentClientCapabilitiesCompletionItemTagSupport = CompletionTextDocumentClientCapabilitiesItemTagSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesDeclaration alias of DeclarationTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DeclarationTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesDeclaration = DeclarationTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesDefinition alias of DefinitionTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DefinitionTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesDefinition = DefinitionTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesDocumentHighlight alias of DocumentHighlightClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentHighlightClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesDocumentHighlight = DocumentHighlightClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesDocumentLink alias of DocumentLinkClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentLinkClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesDocumentLink = DocumentLinkClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesDocumentSymbol alias of DocumentSymbolClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentSymbolClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesDocumentSymbol = DocumentSymbolClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesDocumentSymbolTagSupport alias of DocumentSymbolClientCapabilitiesTagSupport.
|
||||
//
|
||||
// Deprecated: Use DocumentSymbolClientCapabilitiesTagSupport instead.
|
||||
type TextDocumentClientCapabilitiesDocumentSymbolTagSupport = DocumentSymbolClientCapabilitiesTagSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesFoldingRange alias of FoldingRangeClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use FoldingRangeClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesFoldingRange = FoldingRangeClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesFormatting alias of DocumentFormattingClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentFormattingClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesFormatting = DocumentFormattingClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesHover alias of HoverTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use HoverTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesHover = HoverTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesImplementation alias of ImplementationTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use ImplementationTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesImplementation = ImplementationTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesLinkedEditingRange alias of LinkedEditingRangeClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use LinkedEditingRangeClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesLinkedEditingRange = LinkedEditingRangeClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesMoniker of MonikerClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use MonikerClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesMoniker = MonikerClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesOnTypeFormatting of DocumentOnTypeFormattingClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentOnTypeFormattingClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesOnTypeFormatting = DocumentOnTypeFormattingClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesPublishDiagnostics of PublishDiagnosticsClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use PublishDiagnosticsClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesPublishDiagnostics = PublishDiagnosticsClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesPublishDiagnosticsTagSupport of PublishDiagnosticsClientCapabilitiesTagSupport.
|
||||
//
|
||||
// Deprecated: Use PublishDiagnosticsClientCapabilitiesTagSupport instead.
|
||||
type TextDocumentClientCapabilitiesPublishDiagnosticsTagSupport = PublishDiagnosticsClientCapabilitiesTagSupport
|
||||
|
||||
// TextDocumentClientCapabilitiesRangeFormatting of DocumentRangeFormattingClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DocumentRangeFormattingClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesRangeFormatting = DocumentRangeFormattingClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesReferences of ReferencesTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use ReferencesTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesReferences = ReferencesTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesRename of RenameClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use RenameClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesRename = RenameClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesSelectionRange of SelectionRangeClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use SelectionRangeClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesSelectionRange = SelectionRangeClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesSemanticTokens of SemanticTokensClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use SemanticTokensClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesSemanticTokens = SemanticTokensClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesSignatureHelp of SignatureHelpTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use SignatureHelpTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesSignatureHelp = SignatureHelpTextDocumentClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesSynchronization of TextDocumentSyncClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use TextDocumentSyncClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesSynchronization = TextDocumentSyncClientCapabilities
|
||||
|
||||
// TextDocumentClientCapabilitiesTypeDefinition of TypeDefinitionTextDocumentClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use TypeDefinitionTextDocumentClientCapabilities instead.
|
||||
type TextDocumentClientCapabilitiesTypeDefinition = TypeDefinitionTextDocumentClientCapabilities
|
||||
|
||||
// Abort alias of FailureHandlingKindAbort.
|
||||
//
|
||||
// Deprecated: Use FailureHandlingKindAbort instead.
|
||||
const Abort = FailureHandlingKindAbort
|
||||
|
||||
// TextOnlyTransactional alias of FailureHandlingKindTextOnlyTransactional.
|
||||
//
|
||||
// Deprecated: Use FailureHandlingKindTextOnlyTransactional instead.
|
||||
const TextOnlyTransactional = FailureHandlingKindTextOnlyTransactional
|
||||
|
||||
// Transactional alias of FailureHandlingKindTransactional.
|
||||
//
|
||||
// Deprecated: Use FailureHandlingKindTransactional instead.
|
||||
const Transactional = FailureHandlingKindTransactional
|
||||
|
||||
// Undo alias of FailureHandlingKindUndo.
|
||||
//
|
||||
// Deprecated: Use FailureHandlingKindUndo instead.
|
||||
const Undo = FailureHandlingKindUndo
|
||||
|
||||
// WorkspaceClientCapabilitiesSymbol alias of WorkspaceSymbolClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use WorkspaceSymbolClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesSymbol = WorkspaceSymbolClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesSymbolKind alias of SymbolKindCapabilities.
|
||||
//
|
||||
// Deprecated: Use SymbolKindCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesSymbolKind = SymbolKindCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesCodeLens alias of CodeLensWorkspaceClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use CodeLensWorkspaceClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesCodeLens = CodeLensWorkspaceClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesDidChangeConfiguration alias of DidChangeConfigurationWorkspaceClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DidChangeConfigurationWorkspaceClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesDidChangeConfiguration = DidChangeConfigurationWorkspaceClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesDidChangeWatchedFiles alias of DidChangeWatchedFilesWorkspaceClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use DidChangeWatchedFilesWorkspaceClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesDidChangeWatchedFiles = DidChangeWatchedFilesWorkspaceClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesExecuteCommand alias of ExecuteCommandClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use ExecuteCommandClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesExecuteCommand = ExecuteCommandClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesSemanticTokens alias of SemanticTokensWorkspaceClientCapabilities.
|
||||
//
|
||||
// Deprecated: Use SemanticTokensWorkspaceClientCapabilities instead.
|
||||
type WorkspaceClientCapabilitiesSemanticTokens = SemanticTokensWorkspaceClientCapabilities
|
||||
|
||||
// WorkspaceClientCapabilitiesSemanticTokensRequests alias of SemanticTokensWorkspaceClientCapabilitiesRequests.
|
||||
//
|
||||
// Deprecated: Use SemanticTokensWorkspaceClientCapabilitiesRequests instead.
|
||||
type WorkspaceClientCapabilitiesSemanticTokensRequests = SemanticTokensWorkspaceClientCapabilitiesRequests
|
149
templ/lsp/protocol/diagnostics.go
Normal file
149
templ/lsp/protocol/diagnostics.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Diagnostic represents a diagnostic, such as a compiler error or warning.
|
||||
//
|
||||
// Diagnostic objects are only valid in the scope of a resource.
|
||||
type Diagnostic struct {
|
||||
// Range is the range at which the message applies.
|
||||
Range Range `json:"range"`
|
||||
|
||||
// Severity is the diagnostic's severity. Can be omitted. If omitted it is up to the
|
||||
// client to interpret diagnostics as error, warning, info or hint.
|
||||
Severity DiagnosticSeverity `json:"severity,omitempty"`
|
||||
|
||||
// Code is the diagnostic's code, which might appear in the user interface.
|
||||
Code any `json:"code,omitempty"` // int32 | string;
|
||||
|
||||
// CodeDescription an optional property to describe the error code.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
CodeDescription *CodeDescription `json:"codeDescription,omitempty"`
|
||||
|
||||
// Source a human-readable string describing the source of this
|
||||
// diagnostic, e.g. 'typescript' or 'super lint'.
|
||||
Source string `json:"source,omitempty"`
|
||||
|
||||
// Message is the diagnostic's message.
|
||||
Message string `json:"message"`
|
||||
|
||||
// Tags is the additional metadata about the diagnostic.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
Tags []DiagnosticTag `json:"tags,omitempty"`
|
||||
|
||||
// RelatedInformation an array of related diagnostic information, e.g. when symbol-names within
|
||||
// a scope collide all definitions can be marked via this property.
|
||||
RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"`
|
||||
|
||||
// Data is a data entry field that is preserved between a
|
||||
// "textDocument/publishDiagnostics" notification and
|
||||
// "textDocument/codeAction" request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// DiagnosticSeverity indicates the severity of a Diagnostic message.
|
||||
type DiagnosticSeverity float64
|
||||
|
||||
const (
|
||||
// DiagnosticSeverityError reports an error.
|
||||
DiagnosticSeverityError DiagnosticSeverity = 1
|
||||
|
||||
// DiagnosticSeverityWarning reports a warning.
|
||||
DiagnosticSeverityWarning DiagnosticSeverity = 2
|
||||
|
||||
// DiagnosticSeverityInformation reports an information.
|
||||
DiagnosticSeverityInformation DiagnosticSeverity = 3
|
||||
|
||||
// DiagnosticSeverityHint reports a hint.
|
||||
DiagnosticSeverityHint DiagnosticSeverity = 4
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d DiagnosticSeverity) String() string {
|
||||
switch d {
|
||||
case DiagnosticSeverityError:
|
||||
return "Error"
|
||||
case DiagnosticSeverityWarning:
|
||||
return "Warning"
|
||||
case DiagnosticSeverityInformation:
|
||||
return "Information"
|
||||
case DiagnosticSeverityHint:
|
||||
return "Hint"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(d), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// CodeDescription is the structure to capture a description for an error code.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CodeDescription struct {
|
||||
// Href an URI to open with more information about the diagnostic error.
|
||||
Href URI `json:"href"`
|
||||
}
|
||||
|
||||
// DiagnosticTag is the diagnostic tags.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DiagnosticTag float64
|
||||
|
||||
// list of DiagnosticTag.
|
||||
const (
|
||||
// DiagnosticTagUnnecessary unused or unnecessary code.
|
||||
//
|
||||
// Clients are allowed to render diagnostics with this tag faded out instead of having
|
||||
// an error squiggle.
|
||||
DiagnosticTagUnnecessary DiagnosticTag = 1
|
||||
|
||||
// DiagnosticTagDeprecated deprecated or obsolete code.
|
||||
//
|
||||
// Clients are allowed to rendered diagnostics with this tag strike through.
|
||||
DiagnosticTagDeprecated DiagnosticTag = 2
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d DiagnosticTag) String() string {
|
||||
switch d {
|
||||
case DiagnosticTagUnnecessary:
|
||||
return "Unnecessary"
|
||||
case DiagnosticTagDeprecated:
|
||||
return "Deprecated"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(d), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// DiagnosticRelatedInformation represents a related message and source code location for a diagnostic.
|
||||
//
|
||||
// This should be used to point to code locations that cause or related to a diagnostics, e.g when duplicating
|
||||
// a symbol in a scope.
|
||||
type DiagnosticRelatedInformation struct {
|
||||
// Location is the location of this related diagnostic information.
|
||||
Location Location `json:"location"`
|
||||
|
||||
// Message is the message of this related diagnostic information.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PublishDiagnosticsParams represents a params of PublishDiagnostics notification.
|
||||
type PublishDiagnosticsParams struct {
|
||||
// URI is the URI for which diagnostic information is reported.
|
||||
URI DocumentURI `json:"uri"`
|
||||
|
||||
// Version optional the version number of the document the diagnostics are published for.
|
||||
//
|
||||
// @since 3.15
|
||||
Version uint32 `json:"version,omitempty"`
|
||||
|
||||
// Diagnostics an array of diagnostic information items.
|
||||
Diagnostics []Diagnostic `json:"diagnostics"`
|
||||
}
|
640
templ/lsp/protocol/diagnostics_test.go
Normal file
640
templ/lsp/protocol/diagnostics_test.go
Normal file
@@ -0,0 +1,640 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/a-h/templ/lsp/uri"
|
||||
)
|
||||
|
||||
func TestDiagnostic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"severity":1,"code":"foo/bar","codeDescription":{"href":"file:///path/to/test.go"},"source":"test foo bar","message":"foo bar","tags":[1,2],"relatedInformation":[{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}}},"message":"basic_gen.go"}],"data":"testData"}`
|
||||
wantNilSeverity = `{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"code":"foo/bar","codeDescription":{"href":"file:///path/to/test.go"},"source":"test foo bar","message":"foo bar","relatedInformation":[{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}}},"message":"basic_gen.go"}],"data":"testData"}`
|
||||
wantNilCode = `{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"severity":1,"codeDescription":{"href":"file:///path/to/test.go"},"source":"test foo bar","message":"foo bar","relatedInformation":[{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}}},"message":"basic_gen.go"}],"data":"testData"}`
|
||||
wantNilRelatedInformation = `{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"severity":1,"code":"foo/bar","codeDescription":{"href":"file:///path/to/test.go"},"source":"test foo bar","message":"foo bar","data":"testData"}`
|
||||
wantNilAll = `{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"message":"foo bar"}`
|
||||
wantInvalid = `{"range":{"start":{"line":2,"character":1},"end":{"line":3,"character":2}},"severity":1,"code":"foo/bar","codeDescription":{"href":"file:///path/to/test.go"},"source":"test foo bar","message":"foo bar","relatedInformation":[{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":2,"character":1},"end":{"line":3,"character":2}}},"message":"basic_gen.go"}],"data":"invalidData"}`
|
||||
)
|
||||
wantType := Diagnostic{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Severity: DiagnosticSeverityError,
|
||||
Code: "foo/bar",
|
||||
CodeDescription: &CodeDescription{
|
||||
Href: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Source: "test foo bar",
|
||||
Message: "foo bar",
|
||||
Tags: []DiagnosticTag{
|
||||
DiagnosticTagUnnecessary,
|
||||
DiagnosticTagDeprecated,
|
||||
},
|
||||
RelatedInformation: []DiagnosticRelatedInformation{
|
||||
{
|
||||
Location: Location{
|
||||
URI: uri.File("/path/to/basic.go"),
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "basic_gen.go",
|
||||
},
|
||||
},
|
||||
Data: "testData",
|
||||
}
|
||||
wantTypeNilSeverity := Diagnostic{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Code: "foo/bar",
|
||||
CodeDescription: &CodeDescription{
|
||||
Href: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Source: "test foo bar",
|
||||
Message: "foo bar",
|
||||
RelatedInformation: []DiagnosticRelatedInformation{
|
||||
{
|
||||
Location: Location{
|
||||
URI: uri.File("/path/to/basic.go"),
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "basic_gen.go",
|
||||
},
|
||||
},
|
||||
Data: "testData",
|
||||
}
|
||||
wantTypeNilCode := Diagnostic{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Severity: DiagnosticSeverityError,
|
||||
CodeDescription: &CodeDescription{
|
||||
Href: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Source: "test foo bar",
|
||||
Message: "foo bar",
|
||||
RelatedInformation: []DiagnosticRelatedInformation{
|
||||
{
|
||||
Location: Location{
|
||||
URI: uri.File("/path/to/basic.go"),
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "basic_gen.go",
|
||||
},
|
||||
},
|
||||
Data: "testData",
|
||||
}
|
||||
wantTypeNilRelatedInformation := Diagnostic{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Severity: DiagnosticSeverityError,
|
||||
Code: "foo/bar",
|
||||
CodeDescription: &CodeDescription{
|
||||
Href: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Source: "test foo bar",
|
||||
Message: "foo bar",
|
||||
Data: "testData",
|
||||
}
|
||||
wantTypeNilAll := Diagnostic{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Message: "foo bar",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field Diagnostic
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilSeverity",
|
||||
field: wantTypeNilSeverity,
|
||||
want: wantNilSeverity,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilCode",
|
||||
field: wantTypeNilCode,
|
||||
want: wantNilCode,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilRelatedInformation",
|
||||
field: wantTypeNilRelatedInformation,
|
||||
want: wantNilRelatedInformation,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNilAll,
|
||||
want: wantNilAll,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want Diagnostic
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilSeverity",
|
||||
field: wantNilSeverity,
|
||||
want: wantTypeNilSeverity,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilCode",
|
||||
field: wantNilCode,
|
||||
want: wantTypeNilCode,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilRelatedInformation",
|
||||
field: wantNilRelatedInformation,
|
||||
want: wantTypeNilRelatedInformation,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNilAll,
|
||||
want: wantTypeNilAll,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got Diagnostic
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDiagnosticSeverity_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
d DiagnosticSeverity
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Error",
|
||||
d: DiagnosticSeverityError,
|
||||
want: "Error",
|
||||
},
|
||||
{
|
||||
name: "Warning",
|
||||
d: DiagnosticSeverityWarning,
|
||||
want: "Warning",
|
||||
},
|
||||
{
|
||||
name: "Information",
|
||||
d: DiagnosticSeverityInformation,
|
||||
want: "Information",
|
||||
},
|
||||
{
|
||||
name: "Hint",
|
||||
d: DiagnosticSeverityHint,
|
||||
want: "Hint",
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
d: DiagnosticSeverity(0),
|
||||
want: "0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.d.String(); got != tt.want {
|
||||
t.Errorf("DiagnosticSeverity.String() = %v, want %v", tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiagnosticTag_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
d DiagnosticTag
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Unnecessary",
|
||||
d: DiagnosticTagUnnecessary,
|
||||
want: "Unnecessary",
|
||||
},
|
||||
{
|
||||
name: "Deprecated",
|
||||
d: DiagnosticTagDeprecated,
|
||||
want: "Deprecated",
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
d: DiagnosticTag(0),
|
||||
want: "0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.d.String(); got != tt.want {
|
||||
t.Errorf("DiagnosticSeverity.String() = %v, want %v", tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiagnosticRelatedInformation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}}},"message":"basic_gen.go"}`
|
||||
wantInvalid = `{"location":{"uri":"file:///path/to/basic.go","range":{"start":{"line":2,"character":1},"end":{"line":3,"character":2}}},"message":"basic_gen.go"}`
|
||||
)
|
||||
wantType := DiagnosticRelatedInformation{
|
||||
Location: Location{
|
||||
URI: uri.File("/path/to/basic.go"),
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "basic_gen.go",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field DiagnosticRelatedInformation
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want DiagnosticRelatedInformation
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got DiagnosticRelatedInformation
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPublishDiagnosticsParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"uri":"file:///path/to/diagnostics.go","version":1,"diagnostics":[{"range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}},"severity":1,"code":"foo/bar","source":"test foo bar","message":"foo bar","relatedInformation":[{"location":{"uri":"file:///path/to/diagnostics.go","range":{"start":{"line":25,"character":1},"end":{"line":27,"character":3}}},"message":"diagnostics.go"}]}]}`
|
||||
wantInvalid = `{"uri":"file:///path/to/diagnostics_gen.go","version":2,"diagnostics":[{"range":{"start":{"line":2,"character":1},"end":{"line":3,"character":2}},"severity":1,"code":"foo/bar","source":"test foo bar","message":"foo bar","relatedInformation":[{"location":{"uri":"file:///path/to/diagnostics_gen.go","range":{"start":{"line":2,"character":1},"end":{"line":3,"character":2}}},"message":"diagnostics_gen.go"}]}]}`
|
||||
)
|
||||
wantType := PublishDiagnosticsParams{
|
||||
URI: DocumentURI("file:///path/to/diagnostics.go"),
|
||||
Version: 1,
|
||||
Diagnostics: []Diagnostic{
|
||||
{
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
Severity: DiagnosticSeverityError,
|
||||
Code: "foo/bar",
|
||||
Source: "test foo bar",
|
||||
Message: "foo bar",
|
||||
RelatedInformation: []DiagnosticRelatedInformation{
|
||||
{
|
||||
Location: Location{
|
||||
URI: uri.File("/path/to/diagnostics.go"),
|
||||
Range: Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 27,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "diagnostics.go",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field PublishDiagnosticsParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want PublishDiagnosticsParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got PublishDiagnosticsParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
23
templ/lsp/protocol/doc.go
Normal file
23
templ/lsp/protocol/doc.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package protocol implements Language Server Protocol specification in Go.
|
||||
//
|
||||
// This package contains the structs that map directly to the wire format
|
||||
// of the Language Server Protocol.
|
||||
//
|
||||
// It is a literal transcription, with unmodified comments, and only the changes
|
||||
// required to make it Go code.
|
||||
//
|
||||
// - Names are uppercased to export them.
|
||||
//
|
||||
// - All fields have JSON tags added to correct the names.
|
||||
//
|
||||
// - Fields marked with a ? are also marked as "omitempty".
|
||||
//
|
||||
// - Fields that are "|| null" are made pointers.
|
||||
//
|
||||
// - Fields that are string or number are left as string.
|
||||
//
|
||||
// - Fields that are type "number" are made float64.
|
||||
package protocol // import "github.com/a-h/templ/lsp/protocol"
|
40
templ/lsp/protocol/errors.go
Normal file
40
templ/lsp/protocol/errors.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import "github.com/a-h/templ/lsp/jsonrpc2"
|
||||
|
||||
const (
|
||||
// LSPReservedErrorRangeStart is the start range of LSP reserved error codes.
|
||||
//
|
||||
// It doesn't denote a real error code.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
LSPReservedErrorRangeStart jsonrpc2.Code = -32899
|
||||
|
||||
// ContentModified is the state change that invalidates the result of a request in execution.
|
||||
//
|
||||
// Defined by the protocol.
|
||||
CodeContentModified jsonrpc2.Code = -32801
|
||||
|
||||
// RequestCancelled is the cancellation error.
|
||||
//
|
||||
// Defined by the protocol.
|
||||
CodeRequestCancelled jsonrpc2.Code = -32800
|
||||
|
||||
// LSPReservedErrorRangeEnd is the end range of LSP reserved error codes.
|
||||
//
|
||||
// It doesn't denote a real error code.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
LSPReservedErrorRangeEnd jsonrpc2.Code = -32800
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrContentModified should be used when a request is canceled early.
|
||||
ErrContentModified = jsonrpc2.NewError(CodeContentModified, "cancelled JSON-RPC")
|
||||
|
||||
// ErrRequestCancelled should be used when a request is canceled early.
|
||||
ErrRequestCancelled = jsonrpc2.NewError(CodeRequestCancelled, "cancelled JSON-RPC")
|
||||
)
|
461
templ/lsp/protocol/general.go
Normal file
461
templ/lsp/protocol/general.go
Normal file
@@ -0,0 +1,461 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// TraceValue represents a InitializeParams Trace mode.
|
||||
type TraceValue string
|
||||
|
||||
// list of TraceValue.
|
||||
const (
|
||||
// TraceOff disable tracing.
|
||||
TraceOff TraceValue = "off"
|
||||
|
||||
// TraceMessage normal tracing mode.
|
||||
TraceMessage TraceValue = "message"
|
||||
|
||||
// TraceVerbose verbose tracing mode.
|
||||
TraceVerbose TraceValue = "verbose"
|
||||
)
|
||||
|
||||
// ClientInfo information about the client.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ClientInfo struct {
|
||||
// Name is the name of the client as defined by the client.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Version is the client's version as defined by the client.
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// InitializeParams params of Initialize request.
|
||||
type InitializeParams struct {
|
||||
WorkDoneProgressParams
|
||||
|
||||
// ProcessID is the process Id of the parent process that started
|
||||
// the server. Is null if the process has not been started by another process.
|
||||
// If the parent process is not alive then the server should exit (see exit notification) its process.
|
||||
ProcessID int32 `json:"processId"`
|
||||
|
||||
// ClientInfo is the information about the client.
|
||||
//
|
||||
// @since 3.15.0
|
||||
ClientInfo *ClientInfo `json:"clientInfo,omitempty"`
|
||||
|
||||
// Locale is the locale the client is currently showing the user interface
|
||||
// in. This must not necessarily be the locale of the operating
|
||||
// system.
|
||||
//
|
||||
// Uses IETF language tags as the value's syntax
|
||||
// (See https://en.wikipedia.org/wiki/IETF_language_tag)
|
||||
//
|
||||
// @since 3.16.0.
|
||||
Locale string `json:"locale,omitempty"`
|
||||
|
||||
// RootPath is the rootPath of the workspace. Is null
|
||||
// if no folder is open.
|
||||
//
|
||||
// Deprecated: Use RootURI instead.
|
||||
RootPath string `json:"rootPath,omitempty"`
|
||||
|
||||
// RootURI is the rootUri of the workspace. Is null if no
|
||||
// folder is open. If both `rootPath` and "rootUri" are set
|
||||
// "rootUri" wins.
|
||||
//
|
||||
// Deprecated: Use WorkspaceFolders instead.
|
||||
RootURI DocumentURI `json:"rootUri,omitempty"`
|
||||
|
||||
// InitializationOptions user provided initialization options.
|
||||
InitializationOptions any `json:"initializationOptions,omitempty"`
|
||||
|
||||
// Capabilities is the capabilities provided by the client (editor or tool)
|
||||
Capabilities ClientCapabilities `json:"capabilities"`
|
||||
|
||||
// Trace is the initial trace setting. If omitted trace is disabled ('off').
|
||||
Trace TraceValue `json:"trace,omitempty"`
|
||||
|
||||
// WorkspaceFolders is the workspace folders configured in the client when the server starts.
|
||||
// This property is only available if the client supports workspace folders.
|
||||
// It can be `null` if the client supports workspace folders but none are
|
||||
// configured.
|
||||
//
|
||||
// @since 3.6.0.
|
||||
WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders,omitempty"`
|
||||
}
|
||||
|
||||
// InitializeResult result of ClientCapabilities.
|
||||
type InitializeResult struct {
|
||||
// Capabilities is the capabilities the language server provides.
|
||||
Capabilities ServerCapabilities `json:"capabilities"`
|
||||
|
||||
// ServerInfo Information about the server.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
ServerInfo *ServerInfo `json:"serverInfo,omitempty"`
|
||||
}
|
||||
|
||||
// LogTraceParams params of LogTrace notification.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type LogTraceParams struct {
|
||||
// Message is the message to be logged.
|
||||
Message string `json:"message"`
|
||||
|
||||
// Verbose is the additional information that can be computed if the "trace" configuration
|
||||
// is set to "verbose".
|
||||
Verbose TraceValue `json:"verbose,omitempty"`
|
||||
}
|
||||
|
||||
// SetTraceParams params of SetTrace notification.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SetTraceParams struct {
|
||||
// Value is the new value that should be assigned to the trace setting.
|
||||
Value TraceValue `json:"value"`
|
||||
}
|
||||
|
||||
// FileOperationPatternKind is a pattern kind describing if a glob pattern matches a file a folder or
|
||||
// both.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileOperationPatternKind string
|
||||
|
||||
// list of FileOperationPatternKind.
|
||||
const (
|
||||
// FileOperationPatternKindFile is the pattern matches a file only.
|
||||
FileOperationPatternKindFile FileOperationPatternKind = "file"
|
||||
|
||||
// FileOperationPatternKindFolder is the pattern matches a folder only.
|
||||
FileOperationPatternKindFolder FileOperationPatternKind = "folder"
|
||||
)
|
||||
|
||||
// FileOperationPatternOptions matching options for the file operation pattern.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileOperationPatternOptions struct {
|
||||
// IgnoreCase is The pattern should be matched ignoring casing.
|
||||
IgnoreCase bool `json:"ignoreCase,omitempty"`
|
||||
}
|
||||
|
||||
// FileOperationPattern a pattern to describe in which file operation requests or notifications
|
||||
// the server is interested in.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileOperationPattern struct {
|
||||
// The glob pattern to match. Glob patterns can have the following syntax:
|
||||
// - `*` to match one or more characters in a path segment
|
||||
// - `?` to match on one character in a path segment
|
||||
// - `**` to match any number of path segments, including none
|
||||
// - `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript
|
||||
// and JavaScript files)
|
||||
// - `[]` to declare a range of characters to match in a path segment
|
||||
// (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
|
||||
// - `[!...]` to negate a range of characters to match in a path segment
|
||||
// (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but
|
||||
// not `example.0`)
|
||||
Glob string `json:"glob"`
|
||||
|
||||
// Matches whether to match files or folders with this pattern.
|
||||
//
|
||||
// Matches both if undefined.
|
||||
Matches FileOperationPatternKind `json:"matches,omitempty"`
|
||||
|
||||
// Options additional options used during matching.
|
||||
Options FileOperationPatternOptions `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// FileOperationFilter is a filter to describe in which file operation requests or notifications
|
||||
// the server is interested in.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileOperationFilter struct {
|
||||
// Scheme is a URI like "file" or "untitled".
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
|
||||
// Pattern is the actual file operation pattern.
|
||||
Pattern FileOperationPattern `json:"pattern"`
|
||||
}
|
||||
|
||||
// CreateFilesParams is the parameters sent in notifications/requests for user-initiated creation
|
||||
// of files.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type CreateFilesParams struct {
|
||||
// Files an array of all files/folders created in this operation.
|
||||
Files []FileCreate `json:"files"`
|
||||
}
|
||||
|
||||
// FileCreate nepresents information on a file/folder create.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileCreate struct {
|
||||
// URI is a file:// URI for the location of the file/folder being created.
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
// RenameFilesParams is the parameters sent in notifications/requests for user-initiated renames
|
||||
// of files.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type RenameFilesParams struct {
|
||||
// Files an array of all files/folders renamed in this operation. When a folder
|
||||
// is renamed, only the folder will be included, and not its children.
|
||||
Files []FileRename `json:"files"`
|
||||
}
|
||||
|
||||
// FileRename represents information on a file/folder rename.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileRename struct {
|
||||
// OldURI is a file:// URI for the original location of the file/folder being renamed.
|
||||
OldURI string `json:"oldUri"`
|
||||
|
||||
// NewURI is a file:// URI for the new location of the file/folder being renamed.
|
||||
NewURI string `json:"newUri"`
|
||||
}
|
||||
|
||||
// DeleteFilesParams is the parameters sent in notifications/requests for user-initiated deletes
|
||||
// of files.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type DeleteFilesParams struct {
|
||||
// Files an array of all files/folders deleted in this operation.
|
||||
Files []FileDelete `json:"files"`
|
||||
}
|
||||
|
||||
// FileDelete represents information on a file/folder delete.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type FileDelete struct {
|
||||
// URI is a file:// URI for the location of the file/folder being deleted.
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
// DocumentHighlightParams params of DocumentHighlight request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DocumentHighlightParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// DeclarationParams params of Declaration request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DeclarationParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// DefinitionParams params of Definition request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type DefinitionParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// TypeDefinitionParams params of TypeDefinition request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type TypeDefinitionParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// ImplementationParams params of Implementation request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ImplementationParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// ShowDocumentParams params to show a document.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type ShowDocumentParams struct {
|
||||
// URI is the document uri to show.
|
||||
URI URI `json:"uri"`
|
||||
|
||||
// External indicates to show the resource in an external program.
|
||||
// To show for example `https://code.visualstudio.com/`
|
||||
// in the default WEB browser set `external` to `true`.
|
||||
External bool `json:"external,omitempty"`
|
||||
|
||||
// TakeFocus an optional property to indicate whether the editor
|
||||
// showing the document should take focus or not.
|
||||
// Clients might ignore this property if an external
|
||||
// program is started.
|
||||
TakeFocus bool `json:"takeFocus,omitempty"`
|
||||
|
||||
// Selection an optional selection range if the document is a text
|
||||
// document. Clients might ignore the property if an
|
||||
// external program is started or the file is not a text
|
||||
// file.
|
||||
Selection *Range `json:"selection,omitempty"`
|
||||
}
|
||||
|
||||
// ShowDocumentResult is the result of an show document request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type ShowDocumentResult struct {
|
||||
// Success a boolean indicating if the show was successful.
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// ServerInfo Information about the server.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ServerInfo struct {
|
||||
// Name is the name of the server as defined by the server.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Version is the server's version as defined by the server.
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// InitializeError known error codes for an "InitializeError".
|
||||
type InitializeError struct {
|
||||
// Retry indicates whether the client execute the following retry logic:
|
||||
// (1) show the message provided by the ResponseError to the user
|
||||
// (2) user selects retry or cancel
|
||||
// (3) if user selected retry the initialize method is sent again.
|
||||
Retry bool `json:"retry,omitempty"`
|
||||
}
|
||||
|
||||
// ReferencesOptions ReferencesProvider options.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type ReferencesOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// WorkDoneProgressOptions WorkDoneProgress options.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressOptions struct {
|
||||
WorkDoneProgress bool `json:"workDoneProgress,omitempty"`
|
||||
}
|
||||
|
||||
// LinkedEditingRangeParams params for the LinkedEditingRange request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type LinkedEditingRangeParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
}
|
||||
|
||||
// LinkedEditingRanges result of LinkedEditingRange request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type LinkedEditingRanges struct {
|
||||
// Ranges a list of ranges that can be renamed together.
|
||||
//
|
||||
// The ranges must have identical length and contain identical text content.
|
||||
//
|
||||
// The ranges cannot overlap.
|
||||
Ranges []Range `json:"ranges"`
|
||||
|
||||
// WordPattern an optional word pattern (regular expression) that describes valid contents for
|
||||
// the given ranges.
|
||||
//
|
||||
// If no pattern is provided, the client configuration's word pattern will be used.
|
||||
WordPattern string `json:"wordPattern,omitempty"`
|
||||
}
|
||||
|
||||
// MonikerParams params for the Moniker request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type MonikerParams struct {
|
||||
TextDocumentPositionParams
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
}
|
||||
|
||||
// UniquenessLevel is the Moniker uniqueness level to define scope of the moniker.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type UniquenessLevel string
|
||||
|
||||
// list of UniquenessLevel.
|
||||
const (
|
||||
// UniquenessLevelDocument is the moniker is only unique inside a document.
|
||||
UniquenessLevelDocument UniquenessLevel = "document"
|
||||
|
||||
// UniquenessLevelProject is the moniker is unique inside a project for which a dump got created.
|
||||
UniquenessLevelProject UniquenessLevel = "project"
|
||||
|
||||
// UniquenessLevelGroup is the moniker is unique inside the group to which a project belongs.
|
||||
UniquenessLevelGroup UniquenessLevel = "group"
|
||||
|
||||
// UniquenessLevelScheme is the moniker is unique inside the moniker scheme.
|
||||
UniquenessLevelScheme UniquenessLevel = "scheme"
|
||||
|
||||
// UniquenessLevelGlobal is the moniker is globally unique.
|
||||
UniquenessLevelGlobal UniquenessLevel = "global"
|
||||
)
|
||||
|
||||
// MonikerKind is the moniker kind.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type MonikerKind string
|
||||
|
||||
// list of MonikerKind.
|
||||
const (
|
||||
// MonikerKindImport is the moniker represent a symbol that is imported into a project.
|
||||
MonikerKindImport MonikerKind = "import"
|
||||
|
||||
// MonikerKindExport is the moniker represents a symbol that is exported from a project.
|
||||
MonikerKindExport MonikerKind = "export"
|
||||
|
||||
// MonikerKindLocal is the moniker represents a symbol that is local to a project (e.g. a local
|
||||
// variable of a function, a class not visible outside the project, ...).
|
||||
MonikerKindLocal MonikerKind = "local"
|
||||
)
|
||||
|
||||
// Moniker definition to match LSIF 0.5 moniker definition.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type Moniker struct {
|
||||
// Scheme is the scheme of the moniker. For example tsc or .Net.
|
||||
Scheme string `json:"scheme"`
|
||||
|
||||
// Identifier is the identifier of the moniker.
|
||||
//
|
||||
// The value is opaque in LSIF however schema owners are allowed to define the structure if they want.
|
||||
Identifier string `json:"identifier"`
|
||||
|
||||
// Unique is the scope in which the moniker is unique.
|
||||
Unique UniquenessLevel `json:"unique"`
|
||||
|
||||
// Kind is the moniker kind if known.
|
||||
Kind MonikerKind `json:"kind,omitempty"`
|
||||
}
|
||||
|
||||
// StaticRegistrationOptions staticRegistration options to be returned in the initialize request.
|
||||
type StaticRegistrationOptions struct {
|
||||
// ID is the id used to register the request. The id can be used to deregister
|
||||
// the request again. See also Registration#id.
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DocumentLinkRegistrationOptions DocumentLinkRegistration options.
|
||||
type DocumentLinkRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
|
||||
// ResolveProvider document links have a resolve provider as well.
|
||||
ResolveProvider bool `json:"resolveProvider,omitempty"`
|
||||
}
|
||||
|
||||
// InitializedParams params of Initialized notification.
|
||||
type InitializedParams struct{}
|
||||
|
||||
// WorkspaceFolders represents a slice of WorkspaceFolder.
|
||||
type WorkspaceFolders []WorkspaceFolder
|
5206
templ/lsp/protocol/general_test.go
Normal file
5206
templ/lsp/protocol/general_test.go
Normal file
File diff suppressed because it is too large
Load Diff
88
templ/lsp/protocol/handler.go
Normal file
88
templ/lsp/protocol/handler.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
"github.com/a-h/templ/lsp/xcontext"
|
||||
)
|
||||
|
||||
// CancelHandler handler of cancelling.
|
||||
func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
handler, canceller := jsonrpc2.CancelHandler(handler)
|
||||
|
||||
h := func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
if req.Method() != MethodCancelRequest {
|
||||
// TODO(iancottrell): See if we can generate a reply for the request to be cancelled
|
||||
// at the point of cancellation rather than waiting for gopls to naturally reply.
|
||||
// To do that, we need to keep track of whether a reply has been sent already and
|
||||
// be careful about racing between the two paths.
|
||||
// TODO(iancottrell): Add a test that watches the stream and verifies the response
|
||||
// for the cancelled request flows.
|
||||
reply := func(ctx context.Context, resp any, err error) error {
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
|
||||
if ctx.Err() != nil && err == nil {
|
||||
err = ErrRequestCancelled
|
||||
}
|
||||
ctx = xcontext.Detach(ctx)
|
||||
|
||||
return reply(ctx, resp, err)
|
||||
}
|
||||
|
||||
return handler(ctx, reply, req)
|
||||
}
|
||||
|
||||
var params CancelParams
|
||||
if err := json.Unmarshal(req.Params(), ¶ms); err != nil {
|
||||
return replyParseError(ctx, reply, err)
|
||||
}
|
||||
|
||||
switch id := params.ID.(type) {
|
||||
case int32:
|
||||
canceller(jsonrpc2.NewNumberID(id))
|
||||
case string:
|
||||
canceller(jsonrpc2.NewStringID(id))
|
||||
default:
|
||||
return replyParseError(ctx, reply, fmt.Errorf("request ID %v malformed", id))
|
||||
}
|
||||
|
||||
return reply(ctx, nil, nil)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Handlers default jsonrpc2.Handler.
|
||||
func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
return CancelHandler(
|
||||
jsonrpc2.AsyncHandler(
|
||||
jsonrpc2.ReplyHandler(handler),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Call calls method to params and result.
|
||||
func Call(ctx context.Context, conn jsonrpc2.Conn, method string, params, result any) error {
|
||||
id, err := conn.Call(ctx, method, params, result)
|
||||
if ctx.Err() != nil {
|
||||
notifyCancel(ctx, conn, id)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func notifyCancel(ctx context.Context, conn jsonrpc2.Conn, id jsonrpc2.ID) {
|
||||
ctx = xcontext.Detach(ctx)
|
||||
// Note that only *jsonrpc2.ID implements json.Marshaler.
|
||||
_ = conn.Notify(ctx, MethodCancelRequest, &CancelParams{ID: &id})
|
||||
}
|
||||
|
||||
func replyParseError(ctx context.Context, reply jsonrpc2.Replier, err error) error {
|
||||
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
|
||||
}
|
1401
templ/lsp/protocol/language.go
Normal file
1401
templ/lsp/protocol/language.go
Normal file
File diff suppressed because it is too large
Load Diff
6060
templ/lsp/protocol/language_test.go
Normal file
6060
templ/lsp/protocol/language_test.go
Normal file
File diff suppressed because it is too large
Load Diff
119
templ/lsp/protocol/progress.go
Normal file
119
templ/lsp/protocol/progress.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// WorkDoneProgressKind kind of WorkDoneProgress.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressKind string
|
||||
|
||||
// list of WorkDoneProgressKind.
|
||||
const (
|
||||
// WorkDoneProgressKindBegin kind of WorkDoneProgressBegin.
|
||||
WorkDoneProgressKindBegin WorkDoneProgressKind = "begin"
|
||||
|
||||
// WorkDoneProgressKindReport kind of WorkDoneProgressReport.
|
||||
WorkDoneProgressKindReport WorkDoneProgressKind = "report"
|
||||
|
||||
// WorkDoneProgressKindEnd kind of WorkDoneProgressEnd.
|
||||
WorkDoneProgressKindEnd WorkDoneProgressKind = "end"
|
||||
)
|
||||
|
||||
// WorkDoneProgressBegin is the to start progress reporting a "$/progress" notification.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressBegin struct {
|
||||
// Kind is the kind of WorkDoneProgressBegin.
|
||||
//
|
||||
// It must be WorkDoneProgressKindBegin.
|
||||
Kind WorkDoneProgressKind `json:"kind"`
|
||||
|
||||
// Title mandatory title of the progress operation. Used to briefly inform about
|
||||
// the kind of operation being performed.
|
||||
//
|
||||
// Examples: "Indexing" or "Linking dependencies".
|
||||
Title string `json:"title"`
|
||||
|
||||
// Cancellable controls if a cancel button should show to allow the user to cancel the
|
||||
// long running operation. Clients that don't support cancellation are allowed
|
||||
// to ignore the setting.
|
||||
Cancellable bool `json:"cancellable,omitempty"`
|
||||
|
||||
// Message is optional, more detailed associated progress message. Contains
|
||||
// complementary information to the `title`.
|
||||
//
|
||||
// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
|
||||
// If unset, the previous progress message (if any) is still valid.
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// Percentage is optional progress percentage to display (value 100 is considered 100%).
|
||||
// If not provided infinite progress is assumed and clients are allowed
|
||||
// to ignore the `percentage` value in subsequent in report notifications.
|
||||
//
|
||||
// The value should be steadily rising. Clients are free to ignore values
|
||||
// that are not following this rule.
|
||||
Percentage uint32 `json:"percentage,omitempty"`
|
||||
}
|
||||
|
||||
// WorkDoneProgressReport is the reporting progress is done.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressReport struct {
|
||||
// Kind is the kind of WorkDoneProgressReport.
|
||||
//
|
||||
// It must be WorkDoneProgressKindReport.
|
||||
Kind WorkDoneProgressKind `json:"kind"`
|
||||
|
||||
// Cancellable controls enablement state of a cancel button.
|
||||
//
|
||||
// Clients that don't support cancellation or don't support controlling the button's
|
||||
// enablement state are allowed to ignore the property.
|
||||
Cancellable bool `json:"cancellable,omitempty"`
|
||||
|
||||
// Message is optional, more detailed associated progress message. Contains
|
||||
// complementary information to the `title`.
|
||||
//
|
||||
// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
|
||||
// If unset, the previous progress message (if any) is still valid.
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// Percentage is optional progress percentage to display (value 100 is considered 100%).
|
||||
// If not provided infinite progress is assumed and clients are allowed
|
||||
// to ignore the `percentage` value in subsequent in report notifications.
|
||||
//
|
||||
// The value should be steadily rising. Clients are free to ignore values
|
||||
// that are not following this rule.
|
||||
Percentage uint32 `json:"percentage,omitempty"`
|
||||
}
|
||||
|
||||
// WorkDoneProgressEnd is the signaling the end of a progress reporting is done.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressEnd struct {
|
||||
// Kind is the kind of WorkDoneProgressEnd.
|
||||
//
|
||||
// It must be WorkDoneProgressKindEnd.
|
||||
Kind WorkDoneProgressKind `json:"kind"`
|
||||
|
||||
// Message is optional, a final message indicating to for example indicate the outcome
|
||||
// of the operation.
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// WorkDoneProgressParams is a parameter property of report work done progress.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressParams struct {
|
||||
// WorkDoneToken an optional token that a server can use to report work done progress.
|
||||
WorkDoneToken *ProgressToken `json:"workDoneToken,omitempty"`
|
||||
}
|
||||
|
||||
// PartialResultParams is the parameter literal used to pass a partial result token.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type PartialResultParams struct {
|
||||
// PartialResultToken an optional token that a server can use to report partial results
|
||||
// (for example, streaming) to the client.
|
||||
PartialResultToken *ProgressToken `json:"partialResultToken,omitempty"`
|
||||
}
|
509
templ/lsp/protocol/progress_test.go
Normal file
509
templ/lsp/protocol/progress_test.go
Normal file
@@ -0,0 +1,509 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestWorkDoneProgressBegin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"kind":"begin","title":"testTitle","cancellable":true,"message":"testMessage","percentage":30}`
|
||||
wantNil = `{"kind":"begin","title":"testTitle"}`
|
||||
wantInvalid = `{"kind":"invalid","title":"invalidTitle","cancellable":false,"message":"invalidMessage","percentage":0}`
|
||||
)
|
||||
wantType := WorkDoneProgressBegin{
|
||||
Kind: WorkDoneProgressKindBegin,
|
||||
Title: "testTitle",
|
||||
Cancellable: true,
|
||||
Message: "testMessage",
|
||||
Percentage: uint32(30),
|
||||
}
|
||||
wantTypeNil := WorkDoneProgressBegin{
|
||||
Kind: WorkDoneProgressKindBegin,
|
||||
Title: "testTitle",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressBegin
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantTypeNil,
|
||||
want: wantNil,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressBegin
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantNil,
|
||||
want: wantTypeNil,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressBegin
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkDoneProgressReport(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"kind":"report","cancellable":true,"message":"testMessage","percentage":30}`
|
||||
wantNil = `{"kind":"report"}`
|
||||
wantInvalid = `{"kind":"invalid","cancellable":false,"message":"invalidMessage","percentage":0}`
|
||||
)
|
||||
wantType := WorkDoneProgressReport{
|
||||
Kind: WorkDoneProgressKindReport,
|
||||
Cancellable: true,
|
||||
Message: "testMessage",
|
||||
Percentage: uint32(30),
|
||||
}
|
||||
wantTypeNil := WorkDoneProgressReport{
|
||||
Kind: WorkDoneProgressKindReport,
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressReport
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantTypeNil,
|
||||
want: wantNil,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressReport
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantNil,
|
||||
want: wantTypeNil,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressReport
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkDoneProgressEnd(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"kind":"end","message":"testMessage"}`
|
||||
wantNil = `{"kind":"end"}`
|
||||
wantInvalid = `{"kind":"invalid","message":"invalidMessage"}`
|
||||
)
|
||||
wantType := WorkDoneProgressEnd{
|
||||
Kind: WorkDoneProgressKindEnd,
|
||||
Message: "testMessage",
|
||||
}
|
||||
wantTypeNil := WorkDoneProgressEnd{
|
||||
Kind: WorkDoneProgressKindEnd,
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressEnd
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantTypeNil,
|
||||
want: wantNil,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressEnd
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
field: wantNil,
|
||||
want: wantTypeNil,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressEnd
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkDoneProgressParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const wantWorkDoneToken = "156edea9-9d8d-422f-b7ee-81a84594afbb"
|
||||
const want = `{"workDoneToken":"` + wantWorkDoneToken + `"}`
|
||||
|
||||
wantType := WorkDoneProgressParams{
|
||||
WorkDoneToken: NewProgressToken(wantWorkDoneToken),
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if workDoneToken := got.WorkDoneToken; workDoneToken != nil {
|
||||
if diff := cmp.Diff(fmt.Sprint(workDoneToken), wantWorkDoneToken); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPartialResultParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const wantPartialResultParams = "156edea9-9d8d-422f-b7ee-81a84594afbb"
|
||||
const want = `{"partialResultToken":"` + wantPartialResultParams + `"}`
|
||||
|
||||
wantType := PartialResultParams{
|
||||
PartialResultToken: NewProgressToken(wantPartialResultParams),
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field PartialResultParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want PartialResultParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got PartialResultParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if partialResultToken := got.PartialResultToken; partialResultToken != nil {
|
||||
if diff := cmp.Diff(fmt.Sprint(partialResultToken), wantPartialResultParams); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
41
templ/lsp/protocol/protocol.go
Normal file
41
templ/lsp/protocol/protocol.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/a-h/templ/lsp/jsonrpc2"
|
||||
)
|
||||
|
||||
// NewServer returns the context in which client is embedded, jsonrpc2.Conn, and the Client.
|
||||
func NewServer(ctx context.Context, server Server, stream jsonrpc2.Stream, logger *slog.Logger) (context.Context, jsonrpc2.Conn, Client) {
|
||||
conn := jsonrpc2.NewConn(stream)
|
||||
cliint := ClientDispatcher(conn, logger.With(slog.String("name", "client")))
|
||||
ctx = WithClient(ctx, cliint)
|
||||
|
||||
conn.Go(ctx,
|
||||
Handlers(
|
||||
ServerHandler(logger, server, jsonrpc2.MethodNotFoundHandler),
|
||||
),
|
||||
)
|
||||
|
||||
return ctx, conn, cliint
|
||||
}
|
||||
|
||||
// NewClient returns the context in which Client is embedded, jsonrpc2.Conn, and the Server.
|
||||
func NewClient(ctx context.Context, client Client, stream jsonrpc2.Stream, logger *slog.Logger) (context.Context, jsonrpc2.Conn, Server) {
|
||||
ctx = WithClient(ctx, client)
|
||||
|
||||
conn := jsonrpc2.NewConn(stream)
|
||||
conn.Go(ctx,
|
||||
Handlers(
|
||||
ClientHandler(logger, client, jsonrpc2.MethodNotFoundHandler),
|
||||
),
|
||||
)
|
||||
server := ServerDispatcher(conn, logger.With(slog.String("name", "server")))
|
||||
|
||||
return ctx, conn, server
|
||||
}
|
44
templ/lsp/protocol/registration.go
Normal file
44
templ/lsp/protocol/registration.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// Registration general parameters to register for a capability.
|
||||
type Registration struct {
|
||||
// ID is the id used to register the request. The id can be used to deregister
|
||||
// the request again.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Method is the method / capability to register for.
|
||||
Method string `json:"method"`
|
||||
|
||||
// RegisterOptions options necessary for the registration.
|
||||
RegisterOptions any `json:"registerOptions,omitempty"`
|
||||
}
|
||||
|
||||
// RegistrationParams params of Register Capability.
|
||||
type RegistrationParams struct {
|
||||
Registrations []Registration `json:"registrations"`
|
||||
}
|
||||
|
||||
// TextDocumentRegistrationOptions TextDocumentRegistration options.
|
||||
type TextDocumentRegistrationOptions struct {
|
||||
// DocumentSelector a document selector to identify the scope of the registration. If set to null
|
||||
// the document selector provided on the client side will be used.
|
||||
DocumentSelector DocumentSelector `json:"documentSelector"`
|
||||
}
|
||||
|
||||
// Unregistration general parameters to unregister a capability.
|
||||
type Unregistration struct {
|
||||
// ID is the id used to unregister the request or notification. Usually an id
|
||||
// provided during the register request.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Method is the method / capability to unregister for.
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// UnregistrationParams params of Unregistration.
|
||||
type UnregistrationParams struct {
|
||||
Unregisterations []Unregistration `json:"unregisterations"`
|
||||
}
|
579
templ/lsp/protocol/registration_test.go
Normal file
579
templ/lsp/protocol/registration_test.go
Normal file
@@ -0,0 +1,579 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestRegistration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"id":"1","method":"testMethod","registerOptions":{"foo":"bar"}}`
|
||||
wantInterfaces = `{"id":"1","method":"testMethod","registerOptions":["foo","bar"]}`
|
||||
wantNil = `{"id":"1","method":"testMethod"}`
|
||||
wantInvalid = `{"id":"2","method":"invalidMethod","registerOptions":{"baz":"qux"}}`
|
||||
)
|
||||
wantTypeStringInterface := Registration{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
RegisterOptions: map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
wantTypeStringString := Registration{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
RegisterOptions: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
wantTypeInterfaces := Registration{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
RegisterOptions: []any{
|
||||
"foo",
|
||||
"bar",
|
||||
},
|
||||
}
|
||||
wantTypeNil := Registration{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field Registration
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ValidStringInterface",
|
||||
field: wantTypeStringInterface,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidStringString",
|
||||
field: wantTypeStringString,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidInterfaces",
|
||||
field: wantTypeInterfaces,
|
||||
want: wantInterfaces,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNil,
|
||||
want: wantNil,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantTypeStringInterface,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want Registration
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ValidStringInterface",
|
||||
field: want,
|
||||
want: wantTypeStringInterface,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidInterfaces",
|
||||
field: wantInterfaces,
|
||||
want: wantTypeInterfaces,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNil,
|
||||
want: wantTypeNil,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantTypeStringInterface,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got Registration
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegistrationParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"registrations":[{"id":"1","method":"testMethod","registerOptions":{"foo":"bar"}}]}`
|
||||
wantNil = `{"registrations":[{"id":"1","method":"testMethod"}]}`
|
||||
wantInvalid = `{"registrations":[{"id":"2","method":"invalidMethod","registerOptions":{"baz":"qux"}}]}`
|
||||
)
|
||||
wantType := RegistrationParams{
|
||||
Registrations: []Registration{
|
||||
{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
RegisterOptions: map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
wantTypeNil := RegistrationParams{
|
||||
Registrations: []Registration{
|
||||
{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field RegistrationParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNil,
|
||||
want: wantNil,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want RegistrationParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNil,
|
||||
want: wantTypeNil,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got RegistrationParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextDocumentRegistrationOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"documentSelector":[{"language":"go","scheme":"file","pattern":"*.go"},{"language":"cpp","scheme":"untitled","pattern":"*.{cpp,hpp}"}]}`
|
||||
wantInvalid = `{"documentSelector":[{"language":"typescript","scheme":"file","pattern":"*.{ts,js}"},{"language":"c","scheme":"untitled","pattern":"*.{c,h}"}]}`
|
||||
)
|
||||
wantType := TextDocumentRegistrationOptions{
|
||||
DocumentSelector: DocumentSelector{
|
||||
{
|
||||
Language: "go",
|
||||
Scheme: "file",
|
||||
Pattern: "*.go",
|
||||
},
|
||||
{
|
||||
Language: "cpp",
|
||||
Scheme: "untitled",
|
||||
Pattern: "*.{cpp,hpp}",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field TextDocumentRegistrationOptions
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want TextDocumentRegistrationOptions
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got TextDocumentRegistrationOptions
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnregistration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"id":"1","method":"testMethod"}`
|
||||
wantInvalid = `{"id":"2","method":"invalidMethod"}`
|
||||
)
|
||||
wantType := Unregistration{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field Unregistration
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want Unregistration
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got Unregistration
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnregistrationParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"unregisterations":[{"id":"1","method":"testMethod"}]}`
|
||||
wantInvalid = `{"unregisterations":[{"id":"2","method":"invalidMethod"}]}`
|
||||
)
|
||||
wantType := UnregistrationParams{
|
||||
Unregisterations: []Unregistration{
|
||||
{
|
||||
ID: "1",
|
||||
Method: "testMethod",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field UnregistrationParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want UnregistrationParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got UnregistrationParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
110
templ/lsp/protocol/selectionrange.go
Normal file
110
templ/lsp/protocol/selectionrange.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// SelectionRangeProviderOptions selection range provider options interface.
|
||||
type SelectionRangeProviderOptions any
|
||||
|
||||
// SelectionRange represents a selection range represents a part of a selection hierarchy.
|
||||
//
|
||||
// A selection range may have a parent selection range that contains it.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type SelectionRange struct {
|
||||
// Range is the Range of this selection range.
|
||||
Range Range `json:"range"`
|
||||
|
||||
// Parent is the parent selection range containing this range. Therefore `parent.range` must contain this Range.
|
||||
Parent *SelectionRange `json:"parent,omitempty"`
|
||||
}
|
||||
|
||||
// EnableSelectionRange is the whether the selection range.
|
||||
type EnableSelectionRange bool
|
||||
|
||||
// compile time check whether the EnableSelectionRange implements a SelectionRangeProviderOptions interface.
|
||||
var _ SelectionRangeProviderOptions = (*EnableSelectionRange)(nil)
|
||||
|
||||
// Value implements SelectionRangeProviderOptions interface.
|
||||
func (v EnableSelectionRange) Value() any {
|
||||
return bool(v)
|
||||
}
|
||||
|
||||
// NewEnableSelectionRange returns the new EnableSelectionRange underlying types SelectionRangeProviderOptions.
|
||||
func NewEnableSelectionRange(enable bool) SelectionRangeProviderOptions {
|
||||
v := EnableSelectionRange(enable)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// SelectionRangeOptions is the server capability of selection range.
|
||||
type SelectionRangeOptions struct {
|
||||
WorkDoneProgressOptions
|
||||
}
|
||||
|
||||
// compile time check whether the EnableSelectionRange implements a SelectionRangeProviderOptions interface.
|
||||
var _ SelectionRangeProviderOptions = (*EnableSelectionRange)(nil)
|
||||
|
||||
// Value implements SelectionRangeProviderOptions interface.
|
||||
func (v *SelectionRangeOptions) Value() any {
|
||||
return v
|
||||
}
|
||||
|
||||
// NewSelectionRangeOptions returns the new SelectionRangeOptions underlying types SelectionRangeProviderOptions.
|
||||
func NewSelectionRangeOptions(enableWorkDoneProgress bool) SelectionRangeProviderOptions {
|
||||
v := SelectionRangeOptions{
|
||||
WorkDoneProgressOptions: WorkDoneProgressOptions{
|
||||
WorkDoneProgress: enableWorkDoneProgress,
|
||||
},
|
||||
}
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// SelectionRangeRegistrationOptions is the server capability of selection range registration.
|
||||
type SelectionRangeRegistrationOptions struct {
|
||||
SelectionRangeOptions
|
||||
TextDocumentRegistrationOptions
|
||||
StaticRegistrationOptions
|
||||
}
|
||||
|
||||
// compile time check whether the SelectionRangeRegistrationOptions implements a SelectionRangeProviderOptions interface.
|
||||
var _ SelectionRangeProviderOptions = (*SelectionRangeRegistrationOptions)(nil)
|
||||
|
||||
// Value implements SelectionRangeProviderOptions interface.
|
||||
func (v *SelectionRangeRegistrationOptions) Value() any {
|
||||
return v
|
||||
}
|
||||
|
||||
// NewSelectionRangeRegistrationOptions returns the new SelectionRangeRegistrationOptions underlying types SelectionRangeProviderOptions.
|
||||
func NewSelectionRangeRegistrationOptions(enableWorkDoneProgress bool, selector DocumentSelector, id string) SelectionRangeProviderOptions {
|
||||
v := SelectionRangeRegistrationOptions{
|
||||
SelectionRangeOptions: SelectionRangeOptions{
|
||||
WorkDoneProgressOptions: WorkDoneProgressOptions{
|
||||
WorkDoneProgress: enableWorkDoneProgress,
|
||||
},
|
||||
},
|
||||
TextDocumentRegistrationOptions: TextDocumentRegistrationOptions{
|
||||
DocumentSelector: selector,
|
||||
},
|
||||
StaticRegistrationOptions: StaticRegistrationOptions{
|
||||
ID: id,
|
||||
},
|
||||
}
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// SelectionRangeParams represents a parameter literal used in selection range requests.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type SelectionRangeParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// TextDocument is the text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// Positions is the positions inside the text document.
|
||||
Positions []Position `json:"positions"`
|
||||
}
|
179
templ/lsp/protocol/semantic_token.go
Normal file
179
templ/lsp/protocol/semantic_token.go
Normal file
@@ -0,0 +1,179 @@
|
||||
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// SemanticTokenTypes represents a type of semantic token.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokenTypes string
|
||||
|
||||
// list of SemanticTokenTypes.
|
||||
const (
|
||||
SemanticTokenNamespace SemanticTokenTypes = "namespace"
|
||||
|
||||
// Represents a generic type. Acts as a fallback for types which
|
||||
// can't be mapped to a specific type like class or enum.
|
||||
SemanticTokenType SemanticTokenTypes = "type"
|
||||
SemanticTokenClass SemanticTokenTypes = "class"
|
||||
SemanticTokenEnum SemanticTokenTypes = "enum"
|
||||
SemanticTokenInterface SemanticTokenTypes = "interface"
|
||||
SemanticTokenStruct SemanticTokenTypes = "struct"
|
||||
SemanticTokenTypeParameter SemanticTokenTypes = "typeParameter"
|
||||
SemanticTokenParameter SemanticTokenTypes = "parameter"
|
||||
SemanticTokenVariable SemanticTokenTypes = "variable"
|
||||
SemanticTokenProperty SemanticTokenTypes = "property"
|
||||
SemanticTokenEnumMember SemanticTokenTypes = "enumMember"
|
||||
SemanticTokenEvent SemanticTokenTypes = "event"
|
||||
SemanticTokenFunction SemanticTokenTypes = "function"
|
||||
SemanticTokenMethod SemanticTokenTypes = "method"
|
||||
SemanticTokenMacro SemanticTokenTypes = "macro"
|
||||
SemanticTokenKeyword SemanticTokenTypes = "keyword"
|
||||
SemanticTokenModifier SemanticTokenTypes = "modifier"
|
||||
SemanticTokenComment SemanticTokenTypes = "comment"
|
||||
SemanticTokenString SemanticTokenTypes = "string"
|
||||
SemanticTokenNumber SemanticTokenTypes = "number"
|
||||
SemanticTokenRegexp SemanticTokenTypes = "regexp"
|
||||
SemanticTokenOperator SemanticTokenTypes = "operator"
|
||||
)
|
||||
|
||||
// SemanticTokenModifiers represents a modifiers of semantic token.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokenModifiers string
|
||||
|
||||
// list of SemanticTokenModifiers.
|
||||
const (
|
||||
SemanticTokenModifierDeclaration SemanticTokenModifiers = "declaration"
|
||||
SemanticTokenModifierDefinition SemanticTokenModifiers = "definition"
|
||||
SemanticTokenModifierReadonly SemanticTokenModifiers = "readonly"
|
||||
SemanticTokenModifierStatic SemanticTokenModifiers = "static"
|
||||
SemanticTokenModifierDeprecated SemanticTokenModifiers = "deprecated"
|
||||
SemanticTokenModifierAbstract SemanticTokenModifiers = "abstract"
|
||||
SemanticTokenModifierAsync SemanticTokenModifiers = "async"
|
||||
SemanticTokenModifierModification SemanticTokenModifiers = "modification"
|
||||
SemanticTokenModifierDocumentation SemanticTokenModifiers = "documentation"
|
||||
SemanticTokenModifierDefaultLibrary SemanticTokenModifiers = "defaultLibrary"
|
||||
)
|
||||
|
||||
// TokenFormat is an additional token format capability to allow future extensions of the format.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type TokenFormat string
|
||||
|
||||
// TokenFormatRelative described using relative positions.
|
||||
const TokenFormatRelative TokenFormat = "relative"
|
||||
|
||||
// SemanticTokensLegend is the on the capability level types and modifiers are defined using strings.
|
||||
//
|
||||
// However the real encoding happens using numbers.
|
||||
//
|
||||
// The server therefore needs to let the client know which numbers it is using for which types and modifiers.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensLegend struct {
|
||||
// TokenTypes is the token types a server uses.
|
||||
TokenTypes []SemanticTokenTypes `json:"tokenTypes"`
|
||||
|
||||
// TokenModifiers is the token modifiers a server uses.
|
||||
TokenModifiers []SemanticTokenModifiers `json:"tokenModifiers"`
|
||||
}
|
||||
|
||||
// SemanticTokensParams params for the SemanticTokensFull request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// TextDocument is the text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
}
|
||||
|
||||
// SemanticTokens is the result of SemanticTokensFull request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokens struct {
|
||||
// ResultID an optional result id. If provided and clients support delta updating
|
||||
// the client will include the result id in the next semantic token request.
|
||||
//
|
||||
// A server can then instead of computing all semantic tokens again simply
|
||||
// send a delta.
|
||||
ResultID string `json:"resultId,omitempty"`
|
||||
|
||||
// Data is the actual tokens.
|
||||
Data []uint32 `json:"data"`
|
||||
}
|
||||
|
||||
// SemanticTokensPartialResult is the partial result of SemanticTokensFull request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensPartialResult struct {
|
||||
// Data is the actual tokens.
|
||||
Data []uint32 `json:"data"`
|
||||
}
|
||||
|
||||
// SemanticTokensDeltaParams params for the SemanticTokensFullDelta request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensDeltaParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// TextDocument is the text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// PreviousResultID is the result id of a previous response.
|
||||
//
|
||||
// The result Id can either point to a full response or a delta response depending on what was received last.
|
||||
PreviousResultID string `json:"previousResultId"`
|
||||
}
|
||||
|
||||
// SemanticTokensDelta result of SemanticTokensFullDelta request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensDelta struct {
|
||||
// ResultID is the result id.
|
||||
//
|
||||
// This field is readonly.
|
||||
ResultID string `json:"resultId,omitempty"`
|
||||
|
||||
// Edits is the semantic token edits to transform a previous result into a new
|
||||
// result.
|
||||
Edits []SemanticTokensEdit `json:"edits"`
|
||||
}
|
||||
|
||||
// SemanticTokensDeltaPartialResult is the partial result of SemanticTokensFullDelta request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensDeltaPartialResult struct {
|
||||
Edits []SemanticTokensEdit `json:"edits"`
|
||||
}
|
||||
|
||||
// SemanticTokensEdit is the semantic token edit.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensEdit struct {
|
||||
// Start is the start offset of the edit.
|
||||
Start uint32 `json:"start"`
|
||||
|
||||
// DeleteCount is the count of elements to remove.
|
||||
DeleteCount uint32 `json:"deleteCount"`
|
||||
|
||||
// Data is the elements to insert.
|
||||
Data []uint32 `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// SemanticTokensRangeParams params for the SemanticTokensRange request.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
type SemanticTokensRangeParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// TextDocument is the text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// Range is the range the semantic tokens are requested for.
|
||||
Range Range `json:"range"`
|
||||
}
|
1893
templ/lsp/protocol/server.go
Normal file
1893
templ/lsp/protocol/server.go
Normal file
File diff suppressed because it is too large
Load Diff
111
templ/lsp/protocol/text.go
Normal file
111
templ/lsp/protocol/text.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// DidOpenTextDocumentParams params of DidOpenTextDocument notification.
|
||||
type DidOpenTextDocumentParams struct {
|
||||
// TextDocument is the document that was opened.
|
||||
TextDocument TextDocumentItem `json:"textDocument"`
|
||||
}
|
||||
|
||||
// DidChangeTextDocumentParams params of DidChangeTextDocument notification.
|
||||
type DidChangeTextDocumentParams struct {
|
||||
// TextDocument is the document that did change. The version number points
|
||||
// to the version after all provided content changes have
|
||||
// been applied.
|
||||
TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// ContentChanges is the actual content changes. The content changes describe single state changes
|
||||
// to the document. So if there are two content changes c1 and c2 for a document
|
||||
// in state S then c1 move the document to S' and c2 to S''.
|
||||
ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` // []TextDocumentContentChangeEvent | text
|
||||
}
|
||||
|
||||
// TextDocumentSaveReason represents reasons why a text document is saved.
|
||||
type TextDocumentSaveReason float64
|
||||
|
||||
const (
|
||||
// TextDocumentSaveReasonManual is the manually triggered, e.g. by the user pressing save, by starting debugging,
|
||||
// or by an API call.
|
||||
TextDocumentSaveReasonManual TextDocumentSaveReason = 1
|
||||
|
||||
// TextDocumentSaveReasonAfterDelay is the automatic after a delay.
|
||||
TextDocumentSaveReasonAfterDelay TextDocumentSaveReason = 2
|
||||
|
||||
// TextDocumentSaveReasonFocusOut when the editor lost focus.
|
||||
TextDocumentSaveReasonFocusOut TextDocumentSaveReason = 3
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (t TextDocumentSaveReason) String() string {
|
||||
switch t {
|
||||
case TextDocumentSaveReasonManual:
|
||||
return "Manual"
|
||||
case TextDocumentSaveReasonAfterDelay:
|
||||
return "AfterDelay"
|
||||
case TextDocumentSaveReasonFocusOut:
|
||||
return "FocusOut"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(t), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// TextDocumentChangeRegistrationOptions describe options to be used when registering for text document change events.
|
||||
type TextDocumentChangeRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
|
||||
// SyncKind how documents are synced to the server. See TextDocumentSyncKind.Full
|
||||
// and TextDocumentSyncKind.Incremental.
|
||||
SyncKind TextDocumentSyncKind `json:"syncKind"`
|
||||
}
|
||||
|
||||
// WillSaveTextDocumentParams is the parameters send in a will save text document notification.
|
||||
type WillSaveTextDocumentParams struct {
|
||||
// TextDocument is the document that will be saved.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// Reason is the 'TextDocumentSaveReason'.
|
||||
Reason TextDocumentSaveReason `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// DidSaveTextDocumentParams params of DidSaveTextDocument notification.
|
||||
type DidSaveTextDocumentParams struct {
|
||||
// Text optional the content when saved. Depends on the includeText value
|
||||
// when the save notification was requested.
|
||||
Text string `json:"text,omitempty"`
|
||||
|
||||
// TextDocument is the document that was saved.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
}
|
||||
|
||||
// TextDocumentContentChangeEvent an event describing a change to a text document. If range and rangeLength are omitted
|
||||
// the new text is considered to be the full content of the document.
|
||||
type TextDocumentContentChangeEvent struct {
|
||||
// Range is the range of the document that changed.
|
||||
Range *Range `json:"range,omitempty"`
|
||||
|
||||
// RangeLength is the length of the range that got replaced.
|
||||
RangeLength uint32 `json:"rangeLength,omitempty"`
|
||||
|
||||
// Text is the new text of the document.
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// TextDocumentSaveRegistrationOptions TextDocumentSave Registration options.
|
||||
type TextDocumentSaveRegistrationOptions struct {
|
||||
TextDocumentRegistrationOptions
|
||||
|
||||
// IncludeText is the client is supposed to include the content on save.
|
||||
IncludeText bool `json:"includeText,omitempty"`
|
||||
}
|
||||
|
||||
// DidCloseTextDocumentParams params of DidCloseTextDocument notification.
|
||||
type DidCloseTextDocumentParams struct {
|
||||
// TextDocument the document that was closed.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
}
|
930
templ/lsp/protocol/text_test.go
Normal file
930
templ/lsp/protocol/text_test.go
Normal file
@@ -0,0 +1,930 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/a-h/templ/lsp/uri"
|
||||
)
|
||||
|
||||
func TestDidOpenTextDocumentParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"textDocument":{"uri":"file:///path/to/basic.go","languageId":"go","version":10,"text":"Go Language"}}`
|
||||
wantInvalid = `{"textDocument":{"uri":"file:///path/to/basic_gen.go","languageId":"cpp","version":10,"text":"C++ Language"}}`
|
||||
)
|
||||
wantType := DidOpenTextDocumentParams{
|
||||
TextDocument: TextDocumentItem{
|
||||
URI: uri.File("/path/to/basic.go"),
|
||||
LanguageID: GoLanguage,
|
||||
Version: int32(10),
|
||||
Text: "Go Language",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field DidOpenTextDocumentParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want DidOpenTextDocumentParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got DidOpenTextDocumentParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDidChangeTextDocumentParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"textDocument":{"uri":"file:///path/to/test.go","version":10},"contentChanges":[{"range":{"start":{"line":25,"character":1},"end":{"line":25,"character":3}},"rangeLength":2,"text":"testText"}]}`
|
||||
wantInvalid = `{"textDocument":{"uri":"file:///path/to/test.go","version":10},"contentChanges":[{"range":{"start":{"line":2,"character":1},"end":{"line":3,"character":4}},"rangeLength":3,"text":"invalidText"}]}`
|
||||
)
|
||||
wantType := DidChangeTextDocumentParams{
|
||||
TextDocument: VersionedTextDocumentIdentifier{
|
||||
TextDocumentIdentifier: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Version: int32(10),
|
||||
},
|
||||
ContentChanges: []TextDocumentContentChangeEvent{
|
||||
{
|
||||
Range: &Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 25,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
RangeLength: 2,
|
||||
Text: "testText",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field DidChangeTextDocumentParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want DidChangeTextDocumentParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got DidChangeTextDocumentParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextDocumentSaveReason_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
k TextDocumentSaveReason
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Manual",
|
||||
k: TextDocumentSaveReasonManual,
|
||||
want: "Manual",
|
||||
},
|
||||
{
|
||||
name: "AfterDelay",
|
||||
k: TextDocumentSaveReasonAfterDelay,
|
||||
want: "AfterDelay",
|
||||
},
|
||||
{
|
||||
name: "FocusOut",
|
||||
k: TextDocumentSaveReasonFocusOut,
|
||||
want: "FocusOut",
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
k: TextDocumentSaveReason(0),
|
||||
want: "0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.k.String(); got != tt.want {
|
||||
t.Errorf("TextDocumentSaveReason.String() = %v, want %v", tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextDocumentContentChangeEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"range":{"start":{"line":25,"character":1},"end":{"line":25,"character":3}},"rangeLength":2,"text":"testText"}`
|
||||
wantInvalid = `{"range":{"start":{"line":2,"character":1},"end":{"line":3,"character":4}},"rangeLength":3,"text":"invalidText"}`
|
||||
wantReplaceAll = `{"text":"replace all"}`
|
||||
)
|
||||
wantType := TextDocumentContentChangeEvent{
|
||||
Range: &Range{
|
||||
Start: Position{
|
||||
Line: 25,
|
||||
Character: 1,
|
||||
},
|
||||
End: Position{
|
||||
Line: 25,
|
||||
Character: 3,
|
||||
},
|
||||
},
|
||||
RangeLength: 2,
|
||||
Text: "testText",
|
||||
}
|
||||
wantReplaceAllType := TextDocumentContentChangeEvent{
|
||||
Text: "replace all",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field TextDocumentContentChangeEvent
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ReplaceAll",
|
||||
field: wantReplaceAllType,
|
||||
want: wantReplaceAll,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want TextDocumentContentChangeEvent
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got TextDocumentContentChangeEvent
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextDocumentChangeRegistrationOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"documentSelector":[{"language":"go","scheme":"file","pattern":"*.go"}],"syncKind":2}`
|
||||
wantInvalid = `{"documentSelector":[{"language":"typescript","scheme":"file","pattern":"*.{ts,js}"}],"syncKind":1}`
|
||||
)
|
||||
wantType := TextDocumentChangeRegistrationOptions{
|
||||
TextDocumentRegistrationOptions: TextDocumentRegistrationOptions{
|
||||
DocumentSelector: DocumentSelector{
|
||||
{
|
||||
Language: "go",
|
||||
Scheme: "file",
|
||||
Pattern: "*.go",
|
||||
},
|
||||
},
|
||||
},
|
||||
SyncKind: 2,
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field TextDocumentChangeRegistrationOptions
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want TextDocumentChangeRegistrationOptions
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got TextDocumentChangeRegistrationOptions
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWillSaveTextDocumentParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"textDocument":{"uri":"file:///path/to/test.go"},"reason":3}`
|
||||
wantNilAll = `{"textDocument":{"uri":"file:///path/to/test.go"}}`
|
||||
wantInvalid = `{"textDocument":{"uri":"file:///path/to/invalid.go"},"reason":1}`
|
||||
)
|
||||
wantType := WillSaveTextDocumentParams{
|
||||
TextDocument: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
Reason: TextDocumentSaveReasonFocusOut,
|
||||
}
|
||||
wantTypeNilAll := WillSaveTextDocumentParams{
|
||||
TextDocument: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WillSaveTextDocumentParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNilAll,
|
||||
want: wantNilAll,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WillSaveTextDocumentParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNilAll,
|
||||
want: wantTypeNilAll,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WillSaveTextDocumentParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDidSaveTextDocumentParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"text":"testText","textDocument":{"uri":"file:///path/to/test.go"}}`
|
||||
wantNilAll = `{"textDocument":{"uri":"file:///path/to/test.go"}}`
|
||||
wantInvalid = `{"text":"invalidText","textDocument":{"uri":"file:///path/to/invalid.go"}}`
|
||||
)
|
||||
wantType := DidSaveTextDocumentParams{
|
||||
Text: "testText",
|
||||
TextDocument: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
}
|
||||
wantTypeNilAll := DidSaveTextDocumentParams{
|
||||
TextDocument: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field DidSaveTextDocumentParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNilAll,
|
||||
want: wantNilAll,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want DidSaveTextDocumentParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNilAll,
|
||||
want: wantTypeNilAll,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got DidSaveTextDocumentParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextDocumentSaveRegistrationOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"documentSelector":[{"language":"go","scheme":"file","pattern":"*.go"}],"includeText":true}`
|
||||
wantNilAll = `{"documentSelector":[{"language":"go","scheme":"file","pattern":"*.go"}]}`
|
||||
wantInvalid = `{"documentSelector":[{"language":"typescript","scheme":"file","pattern":"*.{ts,js}"}],"includeText":false}`
|
||||
)
|
||||
wantType := TextDocumentSaveRegistrationOptions{
|
||||
TextDocumentRegistrationOptions: TextDocumentRegistrationOptions{
|
||||
DocumentSelector: DocumentSelector{
|
||||
{
|
||||
Language: "go",
|
||||
Scheme: "file",
|
||||
Pattern: "*.go",
|
||||
},
|
||||
},
|
||||
},
|
||||
IncludeText: true,
|
||||
}
|
||||
wantTypeNilAll := TextDocumentSaveRegistrationOptions{
|
||||
TextDocumentRegistrationOptions: TextDocumentRegistrationOptions{
|
||||
DocumentSelector: DocumentSelector{
|
||||
{
|
||||
Language: "go",
|
||||
Scheme: "file",
|
||||
Pattern: "*.go",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field TextDocumentSaveRegistrationOptions
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantTypeNilAll,
|
||||
want: wantNilAll,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want TextDocumentSaveRegistrationOptions
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValidNilAll",
|
||||
field: wantNilAll,
|
||||
want: wantTypeNilAll,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got TextDocumentSaveRegistrationOptions
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDidCloseTextDocumentParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"textDocument":{"uri":"file:///path/to/test.go"}}`
|
||||
wantInvalid = `{"textDocument":{"uri":"file:///path/to/invalid.go"}}`
|
||||
)
|
||||
wantType := DidCloseTextDocumentParams{
|
||||
TextDocument: TextDocumentIdentifier{
|
||||
URI: uri.File("/path/to/test.go"),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field DidCloseTextDocumentParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want DidCloseTextDocumentParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got DidCloseTextDocumentParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
9
templ/lsp/protocol/util.go
Normal file
9
templ/lsp/protocol/util.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// NewVersion returns the int32 pointer converted i.
|
||||
func NewVersion(i int32) *int32 {
|
||||
return &i
|
||||
}
|
33
templ/lsp/protocol/util_test.go
Normal file
33
templ/lsp/protocol/util_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
i int32
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
i: 5000,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
want := NewVersion(tt.i)
|
||||
if got := NewVersion(tt.i); *got != *want {
|
||||
t.Errorf("NewVersion(%v) = %v, want %v", tt.i, *got, *want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
7
templ/lsp/protocol/version.go
Normal file
7
templ/lsp/protocol/version.go
Normal file
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
// Version is the version of the language-server-protocol specification being implemented.
|
||||
const Version = "3.15.3"
|
111
templ/lsp/protocol/window.go
Normal file
111
templ/lsp/protocol/window.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import "strconv"
|
||||
|
||||
// ShowMessageParams params of ShowMessage notification.
|
||||
type ShowMessageParams struct {
|
||||
// Message is the actual message.
|
||||
Message string `json:"message"`
|
||||
|
||||
// Type is the message type.
|
||||
Type MessageType `json:"type"`
|
||||
}
|
||||
|
||||
// MessageType type of ShowMessageParams type.
|
||||
type MessageType float64
|
||||
|
||||
const (
|
||||
// MessageTypeError an error message.
|
||||
MessageTypeError MessageType = 1
|
||||
// MessageTypeWarning a warning message.
|
||||
MessageTypeWarning MessageType = 2
|
||||
// MessageTypeInfo an information message.
|
||||
MessageTypeInfo MessageType = 3
|
||||
// MessageTypeLog a log message.
|
||||
MessageTypeLog MessageType = 4
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (m MessageType) String() string {
|
||||
switch m {
|
||||
case MessageTypeError:
|
||||
return "error"
|
||||
case MessageTypeWarning:
|
||||
return "warning"
|
||||
case MessageTypeInfo:
|
||||
return "info"
|
||||
case MessageTypeLog:
|
||||
return "log"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(m), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled reports whether the level is enabled.
|
||||
func (m MessageType) Enabled(level MessageType) bool {
|
||||
return level > 0 && m >= level
|
||||
}
|
||||
|
||||
// messageTypeMap map of MessageTypes.
|
||||
var messageTypeMap = map[string]MessageType{
|
||||
"error": MessageTypeError,
|
||||
"warning": MessageTypeWarning,
|
||||
"info": MessageTypeInfo,
|
||||
"log": MessageTypeLog,
|
||||
}
|
||||
|
||||
// ToMessageType converts level to the MessageType.
|
||||
func ToMessageType(level string) MessageType {
|
||||
mt, ok := messageTypeMap[level]
|
||||
if !ok {
|
||||
return MessageType(0) // unknown
|
||||
}
|
||||
|
||||
return mt
|
||||
}
|
||||
|
||||
// ShowMessageRequestParams params of ShowMessage request.
|
||||
type ShowMessageRequestParams struct {
|
||||
// Actions is the message action items to present.
|
||||
Actions []MessageActionItem `json:"actions"`
|
||||
|
||||
// Message is the actual message
|
||||
Message string `json:"message"`
|
||||
|
||||
// Type is the message type. See {@link MessageType}
|
||||
Type MessageType `json:"type"`
|
||||
}
|
||||
|
||||
// MessageActionItem item of ShowMessageRequestParams action.
|
||||
type MessageActionItem struct {
|
||||
// Title a short title like 'Retry', 'Open Log' etc.
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
// LogMessageParams params of LogMessage notification.
|
||||
type LogMessageParams struct {
|
||||
// Message is the actual message
|
||||
Message string `json:"message"`
|
||||
|
||||
// Type is the message type. See {@link MessageType}
|
||||
Type MessageType `json:"type"`
|
||||
}
|
||||
|
||||
// WorkDoneProgressCreateParams params of WorkDoneProgressCreate request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressCreateParams struct {
|
||||
// Token is the token to be used to report progress.
|
||||
Token ProgressToken `json:"token"`
|
||||
}
|
||||
|
||||
// WorkDoneProgressCreateParams params of WorkDoneProgressCancel request.
|
||||
//
|
||||
// @since 3.15.0.
|
||||
type WorkDoneProgressCancelParams struct {
|
||||
// Token is the token to be used to report progress.
|
||||
Token ProgressToken `json:"token"`
|
||||
}
|
793
templ/lsp/protocol/window_test.go
Normal file
793
templ/lsp/protocol/window_test.go
Normal file
@@ -0,0 +1,793 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestShowMessageParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"message":"error message","type":1}`
|
||||
wantUnknown = `{"message":"unknown message","type":0}`
|
||||
)
|
||||
wantType := ShowMessageParams{
|
||||
Message: "error message",
|
||||
Type: MessageTypeError,
|
||||
}
|
||||
wantTypeUnkonwn := ShowMessageParams{
|
||||
Message: "unknown message",
|
||||
Type: MessageType(0),
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field ShowMessageParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantTypeUnkonwn,
|
||||
want: wantUnknown,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want ShowMessageParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantUnknown,
|
||||
want: wantTypeUnkonwn,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got ShowMessageParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShowMessageRequestParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"actions":[{"title":"Retry"}],"message":"error message","type":1}`
|
||||
wantUnknown = `{"actions":[{"title":"Retry"}],"message":"unknown message","type":0}`
|
||||
)
|
||||
wantType := ShowMessageRequestParams{
|
||||
Actions: []MessageActionItem{
|
||||
{
|
||||
Title: "Retry",
|
||||
},
|
||||
},
|
||||
Message: "error message",
|
||||
Type: MessageTypeError,
|
||||
}
|
||||
wantTypeUnkonwn := ShowMessageRequestParams{
|
||||
Actions: []MessageActionItem{
|
||||
{
|
||||
Title: "Retry",
|
||||
},
|
||||
},
|
||||
Message: "unknown message",
|
||||
Type: MessageType(0),
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field ShowMessageRequestParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantTypeUnkonwn,
|
||||
want: wantUnknown,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want ShowMessageRequestParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantUnknown,
|
||||
want: wantTypeUnkonwn,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got ShowMessageRequestParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMessageActionItem(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"title":"Retry"}`
|
||||
wantOpenLog = `{"title":"Open Log"}`
|
||||
)
|
||||
wantType := MessageActionItem{
|
||||
Title: "Retry",
|
||||
}
|
||||
wantTypeOpenLog := MessageActionItem{
|
||||
Title: "Open Log",
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field MessageActionItem
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantTypeOpenLog,
|
||||
want: wantOpenLog,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want MessageActionItem
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantOpenLog,
|
||||
want: wantTypeOpenLog,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got MessageActionItem
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogMessageParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
want = `{"message":"error message","type":1}`
|
||||
wantUnknown = `{"message":"unknown message","type":0}`
|
||||
)
|
||||
wantType := LogMessageParams{
|
||||
Message: "error message",
|
||||
Type: MessageTypeError,
|
||||
}
|
||||
wantTypeUnknown := LogMessageParams{
|
||||
Message: "unknown message",
|
||||
Type: MessageType(0),
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field LogMessageParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantTypeUnknown,
|
||||
want: wantUnknown,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want LogMessageParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
field: wantUnknown,
|
||||
want: wantTypeUnknown,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got LogMessageParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkDoneProgressCreateParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
wantToken = int32(1569)
|
||||
invalidToken = int32(1348)
|
||||
)
|
||||
var (
|
||||
wantString = `{"token":"` + strconv.FormatInt(int64(wantToken), 10) + `"}`
|
||||
wantInvalidString = `{"token":"` + strconv.FormatInt(int64(invalidToken), 10) + `"}`
|
||||
wantNumber = `{"token":` + strconv.FormatInt(int64(wantToken), 10) + `}`
|
||||
wantInvalidNumber = `{"token":` + strconv.FormatInt(int64(invalidToken), 10) + `}`
|
||||
)
|
||||
token := NewProgressToken(strconv.FormatInt(int64(wantToken), 10))
|
||||
wantTypeString := WorkDoneProgressCreateParams{
|
||||
Token: *token,
|
||||
}
|
||||
numberToken := NewNumberProgressToken(wantToken)
|
||||
wantTypeNumber := WorkDoneProgressCreateParams{
|
||||
Token: *numberToken,
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressCreateParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid/String",
|
||||
field: wantTypeString,
|
||||
want: wantString,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid/Number",
|
||||
field: wantTypeNumber,
|
||||
want: wantNumber,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid/String",
|
||||
field: wantTypeString,
|
||||
want: wantInvalidString,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid/Number",
|
||||
field: wantTypeNumber,
|
||||
want: wantInvalidNumber,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressCreateParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid/String",
|
||||
field: wantString,
|
||||
want: wantTypeString,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid/Number",
|
||||
field: wantNumber,
|
||||
want: wantTypeNumber,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid/String",
|
||||
field: wantInvalidString,
|
||||
want: wantTypeString,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid/Number",
|
||||
field: wantInvalidNumber,
|
||||
want: wantTypeNumber,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressCreateParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(fmt.Sprint(got.Token), strconv.FormatInt(int64(wantToken), 10)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkDoneProgressCancelParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
wantToken = int32(1569)
|
||||
invalidToken = int32(1348)
|
||||
)
|
||||
var (
|
||||
want = `{"token":` + strconv.FormatInt(int64(wantToken), 10) + `}`
|
||||
wantInvalid = `{"token":` + strconv.FormatInt(int64(invalidToken), 10) + `}`
|
||||
)
|
||||
token := NewNumberProgressToken(wantToken)
|
||||
wantType := WorkDoneProgressCancelParams{
|
||||
Token: *token,
|
||||
}
|
||||
|
||||
t.Run("Marshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field WorkDoneProgressCancelParams
|
||||
want string
|
||||
wantMarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: wantType,
|
||||
want: want,
|
||||
wantMarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantType,
|
||||
want: wantInvalid,
|
||||
wantMarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := json.Marshal(&tt.field)
|
||||
if (err != nil) != tt.wantMarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, string(got)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
field string
|
||||
want WorkDoneProgressCancelParams
|
||||
wantUnmarshalErr bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
field: want,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
field: wantInvalid,
|
||||
want: wantType,
|
||||
wantUnmarshalErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got WorkDoneProgressCancelParams
|
||||
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(fmt.Sprint(got.Token), strconv.FormatInt(int64(wantToken), 10)); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMessageType_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
m MessageType
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Error",
|
||||
m: MessageTypeError,
|
||||
want: "error",
|
||||
},
|
||||
{
|
||||
name: "Warning",
|
||||
m: MessageTypeWarning,
|
||||
want: "warning",
|
||||
},
|
||||
{
|
||||
name: "Info",
|
||||
m: MessageTypeInfo,
|
||||
want: "info",
|
||||
},
|
||||
{
|
||||
name: "Log",
|
||||
m: MessageTypeLog,
|
||||
want: "log",
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
m: MessageType(0),
|
||||
want: "0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.m.String(); got != tt.want {
|
||||
t.Errorf("MessageType.String() = %v, want %v", tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageType_Enabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
m MessageType
|
||||
level MessageType
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "ErrorError",
|
||||
m: MessageTypeError,
|
||||
level: MessageTypeError,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ErrorInfo",
|
||||
m: MessageTypeError,
|
||||
level: MessageTypeInfo,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ErrorUnknown",
|
||||
m: MessageTypeError,
|
||||
level: MessageType(0),
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.m.Enabled(tt.level); got != tt.want {
|
||||
t.Errorf("MessageType.Enabled(%v) = %v, want %v", tt.level, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToMessageType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
level string
|
||||
want MessageType
|
||||
}{
|
||||
{
|
||||
name: "Error",
|
||||
level: "error",
|
||||
want: MessageTypeError,
|
||||
},
|
||||
{
|
||||
name: "Warning",
|
||||
level: "warning",
|
||||
want: MessageTypeWarning,
|
||||
},
|
||||
{
|
||||
name: "Info",
|
||||
level: "info",
|
||||
want: MessageTypeInfo,
|
||||
},
|
||||
{
|
||||
name: "Log",
|
||||
level: "log",
|
||||
want: MessageTypeLog,
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
level: "0",
|
||||
want: MessageType(0),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := ToMessageType(tt.level); got != tt.want {
|
||||
t.Errorf("ToMessageType(%v) = %v, want %v", tt.level, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
213
templ/lsp/protocol/workspace.go
Normal file
213
templ/lsp/protocol/workspace.go
Normal file
@@ -0,0 +1,213 @@
|
||||
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/a-h/templ/lsp/uri"
|
||||
)
|
||||
|
||||
// WorkspaceFolder response of Workspace folders request.
|
||||
type WorkspaceFolder struct {
|
||||
// URI is the associated URI for this workspace folder.
|
||||
URI string `json:"uri"`
|
||||
|
||||
// Name is the name of the workspace folder. Used to refer to this
|
||||
// workspace folder in the user interface.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// DidChangeWorkspaceFoldersParams params of DidChangeWorkspaceFolders notification.
|
||||
type DidChangeWorkspaceFoldersParams struct {
|
||||
// Event is the actual workspace folder change event.
|
||||
Event WorkspaceFoldersChangeEvent `json:"event"`
|
||||
}
|
||||
|
||||
// WorkspaceFoldersChangeEvent is the workspace folder change event.
|
||||
type WorkspaceFoldersChangeEvent struct {
|
||||
// Added is the array of added workspace folders
|
||||
Added []WorkspaceFolder `json:"added"`
|
||||
|
||||
// Removed is the array of the removed workspace folders
|
||||
Removed []WorkspaceFolder `json:"removed"`
|
||||
}
|
||||
|
||||
// DidChangeConfigurationParams params of DidChangeConfiguration notification.
|
||||
type DidChangeConfigurationParams struct {
|
||||
// Settings is the actual changed settings
|
||||
Settings any `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
// ConfigurationParams params of Configuration request.
|
||||
type ConfigurationParams struct {
|
||||
Items []ConfigurationItem `json:"items"`
|
||||
}
|
||||
|
||||
// ConfigurationItem a ConfigurationItem consists of the configuration section to ask for and an additional scope URI.
|
||||
// The configuration section ask for is defined by the server and doesn’t necessarily need to correspond to the configuration store used be the client.
|
||||
// So a server might ask for a configuration cpp.formatterOptions but the client stores the configuration in a XML store layout differently.
|
||||
// It is up to the client to do the necessary conversion. If a scope URI is provided the client should return the setting scoped to the provided resource.
|
||||
// If the client for example uses EditorConfig to manage its settings the configuration should be returned for the passed resource URI. If the client can’t provide a configuration setting for a given scope then null need to be present in the returned array.
|
||||
type ConfigurationItem struct {
|
||||
// ScopeURI is the scope to get the configuration section for.
|
||||
ScopeURI uri.URI `json:"scopeUri,omitempty"`
|
||||
|
||||
// Section is the configuration section asked for.
|
||||
Section string `json:"section,omitempty"`
|
||||
}
|
||||
|
||||
// DidChangeWatchedFilesParams params of DidChangeWatchedFiles notification.
|
||||
type DidChangeWatchedFilesParams struct {
|
||||
// Changes is the actual file events.
|
||||
Changes []*FileEvent `json:"changes,omitempty"`
|
||||
}
|
||||
|
||||
// FileEvent an event describing a file change.
|
||||
type FileEvent struct {
|
||||
// Type is the change type.
|
||||
Type FileChangeType `json:"type"`
|
||||
|
||||
// URI is the file's URI.
|
||||
URI uri.URI `json:"uri"`
|
||||
}
|
||||
|
||||
// FileChangeType is the file event type.
|
||||
type FileChangeType float64
|
||||
|
||||
const (
|
||||
// FileChangeTypeCreated is the file got created.
|
||||
FileChangeTypeCreated FileChangeType = 1
|
||||
// FileChangeTypeChanged is the file got changed.
|
||||
FileChangeTypeChanged FileChangeType = 2
|
||||
// FileChangeTypeDeleted is the file got deleted.
|
||||
FileChangeTypeDeleted FileChangeType = 3
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (t FileChangeType) String() string {
|
||||
switch t {
|
||||
case FileChangeTypeCreated:
|
||||
return "Created"
|
||||
case FileChangeTypeChanged:
|
||||
return "Changed"
|
||||
case FileChangeTypeDeleted:
|
||||
return "Deleted"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(t), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// DidChangeWatchedFilesRegistrationOptions describe options to be used when registering for file system change events.
|
||||
type DidChangeWatchedFilesRegistrationOptions struct {
|
||||
// Watchers is the watchers to register.
|
||||
Watchers []FileSystemWatcher `json:"watchers"`
|
||||
}
|
||||
|
||||
// FileSystemWatcher watchers of DidChangeWatchedFiles Registration options.
|
||||
type FileSystemWatcher struct {
|
||||
// GlobPattern is the glob pattern to watch.
|
||||
//
|
||||
// Glob patterns can have the following syntax:
|
||||
// - `*` to match one or more characters in a path segment
|
||||
// - `?` to match on one character in a path segment
|
||||
// - `**` to match any number of path segments, including none
|
||||
// - `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
|
||||
// - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
|
||||
// - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
|
||||
GlobPattern string `json:"globPattern"`
|
||||
|
||||
// Kind is the kind of events of interest. If omitted it defaults
|
||||
// to WatchKind.Create | WatchKind.Change | WatchKind.Delete
|
||||
// which is 7.
|
||||
Kind WatchKind `json:"kind,omitempty"`
|
||||
}
|
||||
|
||||
// WatchKind kind of FileSystemWatcher kind.
|
||||
type WatchKind float64
|
||||
|
||||
const (
|
||||
// WatchKindCreate interested in create events.
|
||||
WatchKindCreate WatchKind = 1
|
||||
|
||||
// WatchKindChange interested in change events.
|
||||
WatchKindChange WatchKind = 2
|
||||
|
||||
// WatchKindDelete interested in delete events.
|
||||
WatchKindDelete WatchKind = 4
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (k WatchKind) String() string {
|
||||
switch k {
|
||||
case WatchKindCreate:
|
||||
return "Create"
|
||||
case WatchKindChange:
|
||||
return "Change"
|
||||
case WatchKindDelete:
|
||||
return "Delete"
|
||||
default:
|
||||
return strconv.FormatFloat(float64(k), 'f', -10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// WorkspaceSymbolParams is the parameters of a Workspace Symbol request.
|
||||
type WorkspaceSymbolParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// Query a query string to filter symbols by.
|
||||
//
|
||||
// Clients may send an empty string here to request all symbols.
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// ExecuteCommandParams params of Execute a command.
|
||||
type ExecuteCommandParams struct {
|
||||
WorkDoneProgressParams
|
||||
|
||||
// Command is the identifier of the actual command handler.
|
||||
Command string `json:"command"`
|
||||
|
||||
// Arguments that the command should be invoked with.
|
||||
Arguments []any `json:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// ExecuteCommandRegistrationOptions execute command registration options.
|
||||
type ExecuteCommandRegistrationOptions struct {
|
||||
// Commands is the commands to be executed on the server
|
||||
Commands []string `json:"commands"`
|
||||
}
|
||||
|
||||
// ApplyWorkspaceEditParams params of Applies a WorkspaceEdit.
|
||||
type ApplyWorkspaceEditParams struct {
|
||||
// Label an optional label of the workspace edit. This label is
|
||||
// presented in the user interface for example on an undo
|
||||
// stack to undo the workspace edit.
|
||||
Label string `json:"label,omitempty"`
|
||||
|
||||
// Edit is the edits to apply.
|
||||
Edit WorkspaceEdit `json:"edit"`
|
||||
}
|
||||
|
||||
// ApplyWorkspaceEditResponse response of Applies a WorkspaceEdit.
|
||||
type ApplyWorkspaceEditResponse struct {
|
||||
// Applied indicates whether the edit was applied or not.
|
||||
Applied bool `json:"applied"`
|
||||
|
||||
// FailureReason an optional textual description for why the edit was not applied.
|
||||
// This may be used by the server for diagnostic logging or to provide
|
||||
// a suitable error for a request that triggered the edit.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
FailureReason string `json:"failureReason,omitempty"`
|
||||
|
||||
// FailedChange depending on the client's failure handling strategy "failedChange"
|
||||
// might contain the index of the change that failed. This property is
|
||||
// only available if the client signals a "failureHandlingStrategy"
|
||||
// in its client capabilities.
|
||||
//
|
||||
// @since 3.16.0.
|
||||
FailedChange uint32 `json:"failedChange,omitempty"`
|
||||
}
|
1697
templ/lsp/protocol/workspace_test.go
Normal file
1697
templ/lsp/protocol/workspace_test.go
Normal file
File diff suppressed because it is too large
Load Diff
192
templ/lsp/uri/uri.go
Normal file
192
templ/lsp/uri/uri.go
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2019 The Go Language Server Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uri
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
// FileScheme schema of filesystem path.
|
||||
FileScheme = "file"
|
||||
|
||||
// HTTPScheme schema of http.
|
||||
HTTPScheme = "http"
|
||||
|
||||
// HTTPSScheme schema of https.
|
||||
HTTPSScheme = "https"
|
||||
)
|
||||
|
||||
const (
|
||||
hierPart = "://"
|
||||
)
|
||||
|
||||
// URI Uniform Resource Identifier (URI) https://tools.ietf.org/html/rfc3986.
|
||||
//
|
||||
// This class is a simple parser which creates the basic component parts
|
||||
// (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
||||
// and encoding.
|
||||
//
|
||||
// foo://example.com:8042/over/there?name=ferret#nose
|
||||
// \_/ \______________/\_________/ \_________/ \__/
|
||||
// | | | | |
|
||||
// scheme authority path query fragment
|
||||
// | _____________________|__
|
||||
// / \ / \
|
||||
// urn:example:animal:ferret:nose
|
||||
type URI string
|
||||
|
||||
// Filename returns the file path for the given URI.
|
||||
// It is an error to call this on a URI that is not a valid filename.
|
||||
func (u URI) Filename() string {
|
||||
filename, err := filename(u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return filepath.FromSlash(filename)
|
||||
}
|
||||
|
||||
func filename(uri URI) (string, error) {
|
||||
u, err := url.ParseRequestURI(string(uri))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse request URI: %w", err)
|
||||
}
|
||||
|
||||
if u.Scheme != FileScheme {
|
||||
return "", fmt.Errorf("only file URIs are supported, got %v", u.Scheme)
|
||||
}
|
||||
|
||||
if isWindowsDriveURI(u.Path) {
|
||||
u.Path = u.Path[1:]
|
||||
}
|
||||
|
||||
return u.Path, nil
|
||||
}
|
||||
|
||||
// New parses and creates a new URI from s.
|
||||
func New(s string) URI {
|
||||
if u, err := url.PathUnescape(s); err == nil {
|
||||
s = u
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, FileScheme+hierPart) {
|
||||
return URI(s)
|
||||
}
|
||||
|
||||
return File(s)
|
||||
}
|
||||
|
||||
// File parses and creates a new filesystem URI from path.
|
||||
func File(path string) URI {
|
||||
const goRootPragma = "$GOROOT"
|
||||
if len(path) >= len(goRootPragma) && strings.EqualFold(goRootPragma, path[:len(goRootPragma)]) {
|
||||
path = runtime.GOROOT() + path[len(goRootPragma):]
|
||||
}
|
||||
|
||||
if !isWindowsDrivePath(path) {
|
||||
if abs, err := filepath.Abs(path); err == nil {
|
||||
path = abs
|
||||
}
|
||||
}
|
||||
|
||||
if isWindowsDrivePath(path) {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
path = filepath.ToSlash(path)
|
||||
u := url.URL{
|
||||
Scheme: FileScheme,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
return URI(u.String())
|
||||
}
|
||||
|
||||
// Parse parses and creates a new URI from s.
|
||||
func Parse(s string) (u URI, err error) {
|
||||
us, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return u, fmt.Errorf("url.Parse: %w", err)
|
||||
}
|
||||
|
||||
switch us.Scheme {
|
||||
case FileScheme:
|
||||
ut := url.URL{
|
||||
Scheme: FileScheme,
|
||||
Path: us.Path,
|
||||
RawPath: filepath.FromSlash(us.Path),
|
||||
}
|
||||
u = URI(ut.String())
|
||||
|
||||
case HTTPScheme, HTTPSScheme:
|
||||
ut := url.URL{
|
||||
Scheme: us.Scheme,
|
||||
Host: us.Host,
|
||||
Path: us.Path,
|
||||
RawQuery: us.Query().Encode(),
|
||||
Fragment: us.Fragment,
|
||||
}
|
||||
u = URI(ut.String())
|
||||
|
||||
default:
|
||||
return u, errors.New("unknown scheme")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// From returns the new URI from args.
|
||||
func From(scheme, authority, path, query, fragment string) URI {
|
||||
switch scheme {
|
||||
case FileScheme:
|
||||
u := url.URL{
|
||||
Scheme: FileScheme,
|
||||
Path: path,
|
||||
RawPath: filepath.FromSlash(path),
|
||||
}
|
||||
return URI(u.String())
|
||||
|
||||
case HTTPScheme, HTTPSScheme:
|
||||
u := url.URL{
|
||||
Scheme: scheme,
|
||||
Host: authority,
|
||||
Path: path,
|
||||
RawQuery: url.QueryEscape(query),
|
||||
Fragment: fragment,
|
||||
}
|
||||
return URI(u.String())
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown scheme: %s", scheme))
|
||||
}
|
||||
}
|
||||
|
||||
// isWindowsDrivePath returns true if the file path is of the form used by Windows.
|
||||
//
|
||||
// We check if the path begins with a drive letter, followed by a ":".
|
||||
func isWindowsDrivePath(path string) bool {
|
||||
if len(path) < 4 {
|
||||
return false
|
||||
}
|
||||
return unicode.IsLetter(rune(path[0])) && path[1] == ':'
|
||||
}
|
||||
|
||||
// isWindowsDriveURI returns true if the file URI is of the format used by
|
||||
// Windows URIs. The url.Parse package does not specially handle Windows paths
|
||||
// (see https://golang.org/issue/6027). We check if the URI path has
|
||||
// a drive prefix (e.g. "/C:"). If so, we trim the leading "/".
|
||||
func isWindowsDriveURI(uri string) bool {
|
||||
if len(uri) < 4 {
|
||||
return false
|
||||
}
|
||||
return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':'
|
||||
}
|
142
templ/lsp/uri/uri_test.go
Normal file
142
templ/lsp/uri/uri_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2019 The Go Language Server Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uri
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
want URI
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ValidFileScheme",
|
||||
path: "/users/me/c#-projects/",
|
||||
want: URI(FileScheme + hierPart + "/users/me/c%23-projects"),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
path: "users-me-c#-projects",
|
||||
want: URI(""),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if diff := cmp.Diff(File(tt.path), tt.want); (diff != "") != tt.wantErr {
|
||||
t.Errorf("%s: (-got, +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s string
|
||||
want URI
|
||||
}{
|
||||
{
|
||||
name: "ValidFileScheme",
|
||||
s: "file://code.visualstudio.com/docs/extensions/overview.md",
|
||||
want: URI(FileScheme + hierPart + "/docs/extensions/overview.md"),
|
||||
},
|
||||
{
|
||||
name: "ValidHTTPScheme",
|
||||
s: "http://code.visualstudio.com/docs/extensions/overview#frag",
|
||||
want: URI(HTTPScheme + hierPart + "code.visualstudio.com/docs/extensions/overview#frag"),
|
||||
},
|
||||
{
|
||||
name: "ValidHTTPSScheme",
|
||||
s: "https://code.visualstudio.com/docs/extensions/overview#frag",
|
||||
want: URI(HTTPSScheme + hierPart + "code.visualstudio.com/docs/extensions/overview#frag"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := Parse(tt.s)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(got, tt.want); diff != "" {
|
||||
t.Errorf("%s: (-got, +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
type args struct {
|
||||
scheme string
|
||||
authority string
|
||||
path string
|
||||
query string
|
||||
fragment string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want URI
|
||||
}{
|
||||
{
|
||||
name: "ValidFileScheme",
|
||||
args: args{
|
||||
scheme: "file",
|
||||
authority: "example.com",
|
||||
path: "/over/there",
|
||||
query: "name=ferret",
|
||||
fragment: "nose",
|
||||
},
|
||||
want: URI(FileScheme + hierPart + "/over/there"),
|
||||
},
|
||||
{
|
||||
name: "ValidHTTPScheme",
|
||||
args: args{
|
||||
scheme: "http",
|
||||
authority: "example.com:8042",
|
||||
path: "/over/there",
|
||||
query: "name=ferret",
|
||||
fragment: "nose",
|
||||
},
|
||||
want: URI(HTTPScheme + hierPart + "example.com:8042/over/there?name%3Dferret#nose"),
|
||||
},
|
||||
{
|
||||
name: "ValidHTTPSScheme",
|
||||
args: args{
|
||||
scheme: "https",
|
||||
authority: "example.com:8042",
|
||||
path: "/over/there",
|
||||
query: "name=ferret",
|
||||
fragment: "nose",
|
||||
},
|
||||
want: URI(HTTPSScheme + hierPart + "example.com:8042/over/there?name%3Dferret#nose"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if diff := cmp.Diff(From(tt.args.scheme, tt.args.authority, tt.args.path, tt.args.query, tt.args.fragment), tt.want); diff != "" {
|
||||
t.Errorf("%s: (-got, +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
22
templ/lsp/xcontext/xcontext.go
Normal file
22
templ/lsp/xcontext/xcontext.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 The Go Language Server Authors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package xcontext is a package to offer the extra functionality we need
|
||||
// from contexts that is not available from the standard context package.
|
||||
package xcontext
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Detach returns a context that keeps all the values of its parent context
|
||||
// but detaches from the cancellation and error handling.
|
||||
func Detach(ctx context.Context) context.Context { return detachedContext{ctx} }
|
||||
|
||||
type detachedContext struct{ parent context.Context }
|
||||
|
||||
func (v detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false }
|
||||
func (v detachedContext) Done() <-chan struct{} { return nil }
|
||||
func (v detachedContext) Err() error { return nil }
|
||||
func (v detachedContext) Value(key any) any { return v.parent.Value(key) }
|
Reference in New Issue
Block a user