2025-10-15 10:12:44 +03:00

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)
}