152 lines
3.0 KiB
Go
152 lines
3.0 KiB
Go
package lsproto
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
"github.com/go-json-experiment/json"
|
|
"github.com/go-json-experiment/json/jsontext"
|
|
)
|
|
|
|
type DocumentUri string // !!!
|
|
|
|
func (uri DocumentUri) FileName() string {
|
|
if strings.HasPrefix(string(uri), "file://") {
|
|
parsed := core.Must(url.Parse(string(uri)))
|
|
if parsed.Host != "" {
|
|
return "//" + parsed.Host + parsed.Path
|
|
}
|
|
return fixWindowsURIPath(parsed.Path)
|
|
}
|
|
|
|
// Leave all other URIs escaped so we can round-trip them.
|
|
|
|
scheme, path, ok := strings.Cut(string(uri), ":")
|
|
if !ok {
|
|
panic(fmt.Sprintf("invalid URI: %s", uri))
|
|
}
|
|
|
|
authority := "ts-nul-authority"
|
|
if rest, ok := strings.CutPrefix(path, "//"); ok {
|
|
authority, path, ok = strings.Cut(rest, "/")
|
|
if !ok {
|
|
panic(fmt.Sprintf("invalid URI: %s", uri))
|
|
}
|
|
}
|
|
|
|
return "^/" + scheme + "/" + authority + "/" + path
|
|
}
|
|
|
|
func (uri DocumentUri) Path(useCaseSensitiveFileNames bool) tspath.Path {
|
|
fileName := uri.FileName()
|
|
return tspath.ToPath(fileName, "", useCaseSensitiveFileNames)
|
|
}
|
|
|
|
func fixWindowsURIPath(path string) string {
|
|
if rest, ok := strings.CutPrefix(path, "/"); ok {
|
|
if volume, rest, ok := tspath.SplitVolumePath(rest); ok {
|
|
return volume + rest
|
|
}
|
|
}
|
|
return path
|
|
}
|
|
|
|
type HasTextDocumentURI interface {
|
|
TextDocumentURI() DocumentUri
|
|
}
|
|
|
|
type URI string // !!!
|
|
|
|
type Method string
|
|
|
|
func unmarshalPtrTo[T any](data []byte) (*T, error) {
|
|
var v T
|
|
if err := json.Unmarshal(data, &v); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal %T: %w", (*T)(nil), err)
|
|
}
|
|
return &v, nil
|
|
}
|
|
|
|
func unmarshalAny(data []byte) (any, error) {
|
|
var v any
|
|
if err := json.Unmarshal(data, &v); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal any: %w", err)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func unmarshalEmpty(data []byte) (any, error) {
|
|
if len(data) != 0 {
|
|
return nil, fmt.Errorf("expected empty, got: %s", string(data))
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func assertOnlyOne(message string, values ...bool) {
|
|
count := 0
|
|
for _, v := range values {
|
|
if v {
|
|
count++
|
|
}
|
|
}
|
|
if count != 1 {
|
|
panic(message)
|
|
}
|
|
}
|
|
|
|
func assertAtMostOne(message string, values ...bool) {
|
|
count := 0
|
|
for _, v := range values {
|
|
if v {
|
|
count++
|
|
}
|
|
}
|
|
if count > 1 {
|
|
panic(message)
|
|
}
|
|
}
|
|
|
|
func ptrTo[T any](v T) *T {
|
|
return &v
|
|
}
|
|
|
|
type requiredProp bool
|
|
|
|
func (v *requiredProp) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
*v = true
|
|
return dec.SkipValue()
|
|
}
|
|
|
|
// Inspired by https://www.youtube.com/watch?v=dab3I-HcTVk
|
|
|
|
type RequestInfo[Params, Resp any] struct {
|
|
_ [0]Params
|
|
_ [0]Resp
|
|
Method Method
|
|
}
|
|
|
|
type NotificationInfo[Params any] struct {
|
|
_ [0]Params
|
|
Method Method
|
|
}
|
|
|
|
type Null struct{}
|
|
|
|
func (Null) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
data, err := dec.ReadValue()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(data) != "null" {
|
|
return fmt.Errorf("expected null, got %s", data)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (Null) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
return enc.WriteToken(jsontext.Null)
|
|
}
|