Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,49 @@ var request protocol.InitializeRequest

All lsprotocol types are in the `protocol` package.

## TODO
- Currently only types are provided. Would be nice to also provide utility functions for creating a request/response.
- the microsoft tests only test for proper unmarshalling. probably should add tests to make sure structs marshal correctly as well.
- I'm not a golang expert. There may be more efficient ways to do things like validation or tagged unions.
## Utilities
This package provides a limited number of utility functions for dealing with jsonrpc messages.

**DecodeMessage**
This is a helper that takes in a full jsonrpc message and returns a message object.

```golang
var content = []byte("Content-Length: 20\r\n\r\n{...etc}")
message, err := protocol.DecodeMessage(content)
```

**SplitMessage**
This is a helper that takes in a full jsonrpc message and returns information about the message.

```golang
var content = []byte("Content-Length: 20\r\n\r\n{...etc}")
messageLengthIncludingTheHeader, contentLength, content, err := protocol.SplitMessage(content)
```

**Split**
This is a helper you can use with scanner to read jsonrpc messages from a stream.

```golang
var scanner = bufio.NewScanner(reader)
scanner.Split(protocol.Split)
for scanner.Scan() {
msg := scanner.Bytes()
// handle message
}
```

**Error**
This is a tiny helper that takes in an error coe and an `error` instance, and returns a `ResponseError`

```golang
responseError := protocol.Error(someErrorCode, err)
```

## Interfaces
The following interfaces are provided by this package:

- `Request` - represents a jsonrpc request
- `Response` - represents a jsonrpc response
- `Notification` - represents a jsonrpc notification
- `Message` - represents a jsonrpc message
- `IncomingMessage` - notification or request. not in the spec but super helpful.
5 changes: 5 additions & 0 deletions lspgenerator/go/base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,17 @@ def generate_base_types(
],
),
)
result.append("type MethodKind string")
result.append(
join(
[
"type Message interface {",
" isMessage()",
"}",
"type IncomingMessage interface {",
" GetMethod() MethodKind",
" GetParams() any",
"}",
"type Request interface {",
" isRequest()",
"}",
Expand Down
18 changes: 11 additions & 7 deletions lspgenerator/go/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,18 @@ def generate_notifications(
type_resolver: TypeResolver,
) -> list[str]:
notifications = sorted(spec.notifications, key=lambda x: x.method)
result = [
"type NotificationMethod string\n",
]
result = []
constants = "const (\n"
constants += '\tUnknownNotificationMethod = ""\n'
constants += '\tUnknownNotificationMethod MethodKind = ""\n'

for notification in notifications:
method = notification.method.replace("$", "Optional")
camel_case_method = method_to_camel_case(method)
constants += f'\t{camel_case_method}Method NotificationMethod = "{notification.method}"\n'
constants += f'\t{camel_case_method}Method MethodKind = "{notification.method}"\n'
constants += ")\n"
result.append(constants)
result.append(
"var NotificationMethodMap = map[string]NotificationMethod{",
"var NotificationMethodMap = map[string]MethodKind{",
)
for notification in notifications:
method = notification.method.replace("$", "Optional")
Expand All @@ -42,14 +40,20 @@ def generate_notifications(
lines_to_comments(notification.documentation),
f"type {notification.typeName} struct {{",
'\tJsonRPC string `json:"jsonrpc"`',
'\tMethod string `json:"method"`',
'\tMethod MethodKind `json:"method"`',
f'\tParams {param_type} `json:"params"`',
]
struct.append("}")
struct.append(f"func (t {notification.typeName}) isMessage() {{}}")
struct.append(
f"func (t {notification.typeName}) isNotification() {{}}",
)
struct.append(
f"func (t {notification.typeName}) GetMethod() MethodKind {{ return t.Method }}",
)
struct.append(
f"func (t {notification.typeName}) GetParams() any {{ return t.Params }}",
)
struct += [
f"func (t *{notification.typeName}) UnmarshalJSON(x []byte) error {{",
" var m map[string]any",
Expand Down
18 changes: 11 additions & 7 deletions lspgenerator/go/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,19 @@ def generate_requests(
) -> list[str]:
requests = sorted(spec.requests, key=lambda x: x.method)

result = [
"type RequestMethod string\n",
]
result = []
constants = "const (\n"
constants += '\tUnknownRequestMethod RequestMethod = ""\n'
constants += '\tUnknownRequestMethod MethodKind = ""\n'

for request in requests:
camel_case_method = method_to_camel_case(request.method)
constants += (
f'\t{camel_case_method}Method RequestMethod = "{request.method}"\n'
f'\t{camel_case_method}Method MethodKind = "{request.method}"\n'
)
constants += ")\n"
result.append(constants)

result.append("var RequestMethodMap = map[string]RequestMethod{")
result.append("var RequestMethodMap = map[string]MethodKind{")
for request in requests:
camel_case_method = method_to_camel_case(request.method)
result.append(
Expand All @@ -45,13 +43,19 @@ def generate_requests(
f"type {request.typeName} struct {{",
'\tJsonRPC string `json:"jsonrpc"`',
'\tID Or2[string, int32] `json:"id"`',
'\tMethod RequestMethod `json:"method"`',
'\tMethod MethodKind `json:"method"`',
f'\tParams {param_type} `json:"params"`',
]

struct.append("}")
struct.append(f"func (t {request.typeName}) isMessage() {{}}")
struct.append(f"func (t {request.typeName}) isRequest() {{}}")
struct.append(
f"func (t {request.typeName}) GetMethod() MethodKind {{ return t.Method }}",
)
struct.append(
f"func (t {request.typeName}) GetParams() any {{ return t.Params }}",
)
struct += [
f"func (t *{request.typeName}) UnmarshalJSON(x []byte) error {{",
" var m map[string]any",
Expand Down
14 changes: 8 additions & 6 deletions protocol/interfaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package protocol

// Compile-time interface compliance checks
var (
_ Message = (*InitializeRequest)(nil)
_ Message = (*InitializedNotification)(nil)
_ Message = (*InitializeResponse)(nil)
_ Request = (*InitializeRequest)(nil)
_ Notification = (*InitializedNotification)(nil)
_ Response = (*InitializeResponse)(nil)
_ Message = (*InitializeRequest)(nil)
_ Message = (*InitializedNotification)(nil)
_ Message = (*InitializeResponse)(nil)
_ Request = (*InitializeRequest)(nil)
_ Notification = (*InitializedNotification)(nil)
_ Response = (*InitializeResponse)(nil)
_ IncomingMessage = (*InitializeRequest)(nil)
_ IncomingMessage = (*InitializedNotification)(nil)
)
Loading
Loading