141 lines
3.6 KiB
Go
141 lines
3.6 KiB
Go
package lsproto
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
// https://microsoft.github.io/language-server-protocol/specifications/base/0.9/specification/
|
|
|
|
var (
|
|
ErrInvalidHeader = errors.New("lsp: invalid header")
|
|
ErrInvalidContentLength = errors.New("lsp: invalid content length")
|
|
ErrNoContentLength = errors.New("lsp: no content length")
|
|
)
|
|
|
|
type BaseReader struct {
|
|
r *bufio.Reader
|
|
}
|
|
|
|
func NewBaseReader(r io.Reader) *BaseReader {
|
|
return &BaseReader{
|
|
r: bufio.NewReader(r),
|
|
}
|
|
}
|
|
|
|
func (r *BaseReader) Read() ([]byte, error) {
|
|
var contentLength int64
|
|
|
|
for {
|
|
line, err := r.r.ReadBytes('\n')
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, io.EOF
|
|
}
|
|
return nil, fmt.Errorf("lsp: read header: %w", err)
|
|
}
|
|
|
|
if bytes.Equal(line, []byte("\r\n")) {
|
|
break
|
|
}
|
|
|
|
key, value, ok := bytes.Cut(line, []byte(":"))
|
|
if !ok {
|
|
return nil, fmt.Errorf("%w: %q", ErrInvalidHeader, line)
|
|
}
|
|
|
|
if bytes.Equal(key, []byte("Content-Length")) {
|
|
contentLength, err = strconv.ParseInt(string(bytes.TrimSpace(value)), 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: parse error: %w", ErrInvalidContentLength, err)
|
|
}
|
|
if contentLength < 0 {
|
|
return nil, fmt.Errorf("%w: negative value %d", ErrInvalidContentLength, contentLength)
|
|
}
|
|
}
|
|
}
|
|
|
|
if contentLength <= 0 {
|
|
return nil, ErrNoContentLength
|
|
}
|
|
|
|
data := make([]byte, contentLength)
|
|
if _, err := io.ReadFull(r.r, data); err != nil {
|
|
return nil, fmt.Errorf("lsp: read content: %w", err)
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
type BaseWriter struct {
|
|
w *bufio.Writer
|
|
}
|
|
|
|
func NewBaseWriter(w io.Writer) *BaseWriter {
|
|
return &BaseWriter{
|
|
w: bufio.NewWriter(w),
|
|
}
|
|
}
|
|
|
|
func (w *BaseWriter) Write(data []byte) error {
|
|
if _, err := fmt.Fprintf(w.w, "Content-Length: %d\r\n\r\n", len(data)); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.w.Write(data); err != nil {
|
|
return err
|
|
}
|
|
return w.w.Flush()
|
|
}
|
|
|
|
type ErrorCode struct { //nolint:errname
|
|
Name string
|
|
Code int32
|
|
}
|
|
|
|
func (e *ErrorCode) Error() string {
|
|
return e.Name
|
|
}
|
|
|
|
var (
|
|
// Defined by JSON-RPC
|
|
ErrParseError = &ErrorCode{"ParseError", -32700}
|
|
ErrInvalidRequest = &ErrorCode{"InvalidRequest", -32600}
|
|
ErrMethodNotFound = &ErrorCode{"MethodNotFound", -32601}
|
|
ErrInvalidParams = &ErrorCode{"InvalidParams", -32602}
|
|
ErrInternalError = &ErrorCode{"InternalError", -32603}
|
|
|
|
// Error code indicating that a server received a notification or
|
|
// request before the server has received the `initialize` request.
|
|
ErrServerNotInitialized = &ErrorCode{"ServerNotInitialized", -32002}
|
|
ErrUnknownErrorCode = &ErrorCode{"UnknownErrorCode", -32001}
|
|
|
|
// A request failed but it was syntactically correct, e.g the
|
|
// method name was known and the parameters were valid. The error
|
|
// message should contain human readable information about why
|
|
// the request failed.
|
|
ErrRequestFailed = &ErrorCode{"RequestFailed", -32803}
|
|
|
|
// The server cancelled the request. This error code should
|
|
// only be used for requests that explicitly support being
|
|
// server cancellable.
|
|
ErrServerCancelled = &ErrorCode{"ServerCancelled", -32802}
|
|
|
|
// The server detected that the content of a document got
|
|
// modified outside normal conditions. A server should
|
|
// NOT send this error code if it detects a content change
|
|
// in it unprocessed messages. The result even computed
|
|
// on an older state might still be useful for the client.
|
|
//
|
|
// If a client decides that a result is not of any use anymore
|
|
// the client should cancel the request.
|
|
ErrContentModified = &ErrorCode{"ContentModified", -32801}
|
|
|
|
// The client has canceled a request and a server has detected
|
|
// the cancel.
|
|
ErrRequestCancelled = &ErrorCode{"RequestCancelled", -32800}
|
|
)
|