mirror of
https://github.com/laravel-ls/protocol.git
synced 2026-06-16 03:54:56 +02:00
add inline hints
This commit is contained in:
parent
7cebe3d818
commit
5fe36eb7d6
2 changed files with 365 additions and 0 deletions
207
inlay_hints.go
Normal file
207
inlay_hints.go
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// MethodTextDocumentInlayHint method name of `textDocument/inlayHint`.
|
||||
MethodTextDocumentInlayHint = "textDocument/inlayHint"
|
||||
)
|
||||
|
||||
// InlayHintParams - Parameters for a `textDocument/inlayHint` request.
|
||||
//
|
||||
// @since 3.17.0
|
||||
//
|
||||
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintParams
|
||||
type InlayHintParams struct {
|
||||
WorkDoneProgressParams
|
||||
PartialResultParams
|
||||
|
||||
// The text document.
|
||||
TextDocument TextDocumentIdentifier `json:"textDocument"`
|
||||
|
||||
// The visible range for which inlay hints should be computed.
|
||||
Range Range `json:"range"`
|
||||
}
|
||||
|
||||
// InlayHintKind - The kind of an inlay hint.
|
||||
//
|
||||
// @since 3.17.0
|
||||
//
|
||||
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintKind
|
||||
type InlayHintKind int
|
||||
|
||||
const (
|
||||
// InlayHintKindType - An inlay hint that shows a type annotation.
|
||||
InlayHintKindType InlayHintKind = 1
|
||||
|
||||
// InlayHintKindParameter - An inlay hint that shows a parameter name.
|
||||
InlayHintKindParameter InlayHintKind = 2
|
||||
)
|
||||
|
||||
// InlayHintTooltip can be a plain string or a MarkupContent object.
|
||||
//
|
||||
// @since 3.17.0
|
||||
type InlayHintTooltip struct {
|
||||
String *string
|
||||
MarkupContent *MarkupContent
|
||||
}
|
||||
|
||||
func (t InlayHintTooltip) MarshalJSON() ([]byte, error) {
|
||||
if t.String != nil {
|
||||
return json.Marshal(*t.String)
|
||||
}
|
||||
if t.MarkupContent != nil {
|
||||
return json.Marshal(t.MarkupContent)
|
||||
}
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
func (t *InlayHintTooltip) UnmarshalJSON(data []byte) error {
|
||||
*t = InlayHintTooltip{}
|
||||
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
t.String = &str
|
||||
return nil
|
||||
}
|
||||
|
||||
var markup MarkupContent
|
||||
if err := json.Unmarshal(data, &markup); err == nil && markup.Kind != "" {
|
||||
t.MarkupContent = &markup
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("invalid InlayHintTooltip: not string, MarkupContent, or null")
|
||||
}
|
||||
|
||||
// InlayHintLabelPart - A segment of an inlay hint label.
|
||||
//
|
||||
// @since 3.17.0
|
||||
//
|
||||
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintLabelPart
|
||||
type InlayHintLabelPart struct {
|
||||
// The mandatory label value.
|
||||
Value string `json:"value"`
|
||||
|
||||
// The tooltip text or markup shown when hovering this label part.
|
||||
Tooltip *InlayHintTooltip `json:"tooltip,omitempty"`
|
||||
|
||||
// A source location for this label part.
|
||||
Location *Location `json:"location,omitempty"`
|
||||
|
||||
// A command associated with this label part.
|
||||
Command *Command `json:"command,omitempty"`
|
||||
}
|
||||
|
||||
// InlayHintLabel can be either a string or a list of label parts.
|
||||
//
|
||||
// @since 3.17.0
|
||||
type InlayHintLabel struct {
|
||||
String *string
|
||||
Parts []InlayHintLabelPart
|
||||
}
|
||||
|
||||
func (l InlayHintLabel) MarshalJSON() ([]byte, error) {
|
||||
if l.String != nil {
|
||||
return json.Marshal(*l.String)
|
||||
}
|
||||
if l.Parts != nil {
|
||||
return json.Marshal(l.Parts)
|
||||
}
|
||||
return nil, errors.New("one of InlayHintLabel.String or InlayHintLabel.Parts needs to be set")
|
||||
}
|
||||
|
||||
func (l *InlayHintLabel) UnmarshalJSON(data []byte) error {
|
||||
*l = InlayHintLabel{}
|
||||
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
l.String = &str
|
||||
return nil
|
||||
}
|
||||
|
||||
var parts []InlayHintLabelPart
|
||||
if err := json.Unmarshal(data, &parts); err == nil {
|
||||
l.Parts = parts
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("invalid InlayHintLabel: not string or []InlayHintLabelPart")
|
||||
}
|
||||
|
||||
// InlayHint represents an inlay hint item.
|
||||
//
|
||||
// @since 3.17.0
|
||||
//
|
||||
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHint
|
||||
type InlayHint struct {
|
||||
// The position of this hint.
|
||||
Position Position `json:"position"`
|
||||
|
||||
// The label of this hint. A human readable string or an array of label parts.
|
||||
Label InlayHintLabel `json:"label"`
|
||||
|
||||
// The kind of this hint.
|
||||
Kind *InlayHintKind `json:"kind,omitempty"`
|
||||
|
||||
// Optional text edits that are performed when accepting this hint.
|
||||
TextEdits []TextEdit `json:"textEdits,omitempty"`
|
||||
|
||||
// The tooltip text when hovering over this hint.
|
||||
Tooltip *InlayHintTooltip `json:"tooltip,omitempty"`
|
||||
|
||||
// Render padding before this hint.
|
||||
PaddingLeft *bool `json:"paddingLeft,omitempty"`
|
||||
|
||||
// Render padding after this hint.
|
||||
PaddingRight *bool `json:"paddingRight,omitempty"`
|
||||
|
||||
// A data entry field preserved between a hint request and resolve request.
|
||||
Data LSPAny `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// InlayHintResponse - Result for a `textDocument/inlayHint` request.
|
||||
//
|
||||
// It is either an array of `InlayHint` or `null`.
|
||||
//
|
||||
// @since 3.17.0
|
||||
//
|
||||
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHint
|
||||
type InlayHintResponse struct {
|
||||
Hints []InlayHint
|
||||
Null bool
|
||||
}
|
||||
|
||||
func (r InlayHintResponse) MarshalJSON() ([]byte, error) {
|
||||
if r.Null {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
if r.Hints == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(r.Hints)
|
||||
}
|
||||
|
||||
func (r *InlayHintResponse) UnmarshalJSON(data []byte) error {
|
||||
*r = InlayHintResponse{}
|
||||
|
||||
if string(data) == "null" {
|
||||
r.Null = true
|
||||
return nil
|
||||
}
|
||||
|
||||
var hints []InlayHint
|
||||
if err := json.Unmarshal(data, &hints); err == nil {
|
||||
r.Hints = hints
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("invalid inlay hint response: not null or []InlayHint")
|
||||
}
|
||||
158
inlay_hints_test.go
Normal file
158
inlay_hints_test.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package protocol_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/laravel-ls/protocol"
|
||||
)
|
||||
|
||||
func Test_InlayHint_ParamsUnmarshalValidJSON(t *testing.T) {
|
||||
data := []byte(`{"textDocument":{"uri":"file:///tmp/main.go"},"range":{"start":{"line":1,"character":2},"end":{"line":1,"character":8}}}`)
|
||||
|
||||
var params protocol.InlayHintParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintParams failed: %v", err)
|
||||
}
|
||||
|
||||
if params.TextDocument.URI != "file:///tmp/main.go" {
|
||||
t.Fatalf("unexpected textDocument URI: %q", params.TextDocument.URI)
|
||||
}
|
||||
|
||||
if params.Range.Start.Line != 1 || params.Range.End.Character != 8 {
|
||||
t.Fatalf("unexpected range: %+v", params.Range)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHintLabel_UnmarshalString(t *testing.T) {
|
||||
var label protocol.InlayHintLabel
|
||||
if err := json.Unmarshal([]byte(`"x:"`), &label); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintLabel string failed: %v", err)
|
||||
}
|
||||
|
||||
if label.String == nil || *label.String != "x:" {
|
||||
t.Fatalf("expected string label 'x:', got %+v", label)
|
||||
}
|
||||
|
||||
if label.Parts != nil {
|
||||
t.Fatalf("expected parts to be nil for string label")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHintLabel_UnmarshalParts(t *testing.T) {
|
||||
data := []byte(`[{"value":"name"},{"value":":","tooltip":"separator"}]`)
|
||||
|
||||
var label protocol.InlayHintLabel
|
||||
if err := json.Unmarshal(data, &label); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintLabel parts failed: %v", err)
|
||||
}
|
||||
|
||||
if len(label.Parts) != 2 {
|
||||
t.Fatalf("expected 2 label parts, got %d", len(label.Parts))
|
||||
}
|
||||
|
||||
if label.Parts[1].Tooltip == nil || label.Parts[1].Tooltip.String == nil || *label.Parts[1].Tooltip.String != "separator" {
|
||||
t.Fatalf("expected second part tooltip string to be set, got %+v", label.Parts[1].Tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHintTooltip_UnmarshalMarkupContent(t *testing.T) {
|
||||
data := []byte(`{"kind":"markdown","value":"**hint**"}`)
|
||||
|
||||
var tooltip protocol.InlayHintTooltip
|
||||
if err := json.Unmarshal(data, &tooltip); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintTooltip markup failed: %v", err)
|
||||
}
|
||||
|
||||
if tooltip.MarkupContent == nil || tooltip.MarkupContent.Kind != protocol.MarkupKindMarkdown {
|
||||
t.Fatalf("expected markdown tooltip, got %+v", tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHint_UnmarshalTypedObject(t *testing.T) {
|
||||
data := []byte(`{
|
||||
"position": {"line": 2, "character": 10},
|
||||
"label": [{"value":"x"},{"value":":","tooltip":{"kind":"plaintext","value":"type separator"}}],
|
||||
"kind": 1,
|
||||
"textEdits": [{"range":{"start":{"line":2,"character":8},"end":{"line":2,"character":9}},"newText":"value"}],
|
||||
"tooltip": "inferred type",
|
||||
"paddingLeft": true,
|
||||
"paddingRight": false,
|
||||
"data": {"id": 42}
|
||||
}`)
|
||||
|
||||
var hint protocol.InlayHint
|
||||
if err := json.Unmarshal(data, &hint); err != nil {
|
||||
t.Fatalf("unmarshal InlayHint failed: %v", err)
|
||||
}
|
||||
|
||||
if hint.Position.Line != 2 || hint.Position.Character != 10 {
|
||||
t.Fatalf("unexpected position: %+v", hint.Position)
|
||||
}
|
||||
|
||||
if hint.Kind == nil || *hint.Kind != protocol.InlayHintKindType {
|
||||
t.Fatalf("expected kind=InlayHintKindType, got %+v", hint.Kind)
|
||||
}
|
||||
|
||||
if hint.Tooltip == nil || hint.Tooltip.String == nil || *hint.Tooltip.String != "inferred type" {
|
||||
t.Fatalf("expected tooltip string 'inferred type', got %+v", hint.Tooltip)
|
||||
}
|
||||
|
||||
if len(hint.TextEdits) != 1 {
|
||||
t.Fatalf("expected 1 text edit, got %d", len(hint.TextEdits))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHintResponse_UnmarshalArrayAndNull(t *testing.T) {
|
||||
var list protocol.InlayHintResponse
|
||||
if err := json.Unmarshal([]byte(`[{"position":{"line":0,"character":1},"label":"x:"}]`), &list); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintResponse array failed: %v", err)
|
||||
}
|
||||
|
||||
if list.Null {
|
||||
t.Fatalf("expected non-null response for array")
|
||||
}
|
||||
|
||||
if len(list.Hints) != 1 {
|
||||
t.Fatalf("expected 1 hint, got %d", len(list.Hints))
|
||||
}
|
||||
|
||||
var nullRes protocol.InlayHintResponse
|
||||
if err := json.Unmarshal([]byte(`null`), &nullRes); err != nil {
|
||||
t.Fatalf("unmarshal InlayHintResponse null failed: %v", err)
|
||||
}
|
||||
|
||||
if !nullRes.Null {
|
||||
t.Fatalf("expected null response flag to be true")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InlayHintResponse_MarshalArrayAndNull(t *testing.T) {
|
||||
response := protocol.InlayHintResponse{
|
||||
Hints: []protocol.InlayHint{{
|
||||
Position: protocol.Position{Line: 0, Character: 0},
|
||||
Label: func() protocol.InlayHintLabel {
|
||||
label := "a"
|
||||
return protocol.InlayHintLabel{String: &label}
|
||||
}(),
|
||||
}},
|
||||
}
|
||||
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal InlayHintResponse array failed: %v", err)
|
||||
}
|
||||
|
||||
if string(data) == "null" {
|
||||
t.Fatalf("expected array JSON, got null")
|
||||
}
|
||||
|
||||
nullData, err := json.Marshal(protocol.InlayHintResponse{Null: true})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal InlayHintResponse null failed: %v", err)
|
||||
}
|
||||
|
||||
if string(nullData) != "null" {
|
||||
t.Fatalf("expected null JSON, got %s", string(nullData))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue