Files
2026-07-13 13:00:08 +08:00

116 lines
2.7 KiB
Go

package lsp
import (
"encoding/json"
"fmt"
"strings"
)
// parseLocations decodes the three shapes textDocument/definition and
// /references may return: Location, Location[], or LocationLink[].
func parseLocations(raw json.RawMessage) []Location {
s := strings.TrimSpace(string(raw))
if s == "" || s == "null" {
return nil
}
if s[0] == '[' {
var arr []Location
if json.Unmarshal(raw, &arr) == nil && len(arr) > 0 && arr[0].URI != "" {
return arr
}
var links []struct {
TargetURI string `json:"targetUri"`
TargetRange Range `json:"targetRange"`
}
if json.Unmarshal(raw, &links) == nil {
out := make([]Location, 0, len(links))
for _, l := range links {
if l.TargetURI != "" {
out = append(out, Location{URI: l.TargetURI, Range: l.TargetRange})
}
}
return out
}
return nil
}
var one Location
if json.Unmarshal(raw, &one) == nil && one.URI != "" {
return []Location{one}
}
return nil
}
// parseHover decodes a Hover.contents, which may be a MarkupContent, a single
// MarkedString, or an array of them.
func parseHover(raw json.RawMessage) string {
var h struct {
Contents json.RawMessage `json:"contents"`
}
if json.Unmarshal(raw, &h) != nil || len(h.Contents) == 0 {
return ""
}
return strings.TrimSpace(markedToText(h.Contents))
}
func markedToText(raw json.RawMessage) string {
s := strings.TrimSpace(string(raw))
if s == "" || s == "null" {
return ""
}
switch s[0] {
case '"':
var str string
_ = json.Unmarshal(raw, &str)
return str
case '{':
var mc struct {
Kind string `json:"kind"`
Value string `json:"value"`
}
if json.Unmarshal(raw, &mc) == nil && mc.Value != "" {
return mc.Value
}
var ms struct {
Language string `json:"language"`
Value string `json:"value"`
}
_ = json.Unmarshal(raw, &ms)
return ms.Value
case '[':
var parts []json.RawMessage
_ = json.Unmarshal(raw, &parts)
var out []string
for _, p := range parts {
if t := markedToText(p); t != "" {
out = append(out, t)
}
}
return strings.Join(out, "\n")
}
return ""
}
var severityName = map[int]string{1: "error", 2: "warning", 3: "info", 4: "hint"}
func formatDiagnostics(rel string, diags []Diagnostic) string {
if len(diags) == 0 {
return "no diagnostics for " + rel
}
var b strings.Builder
fmt.Fprintf(&b, "%d diagnostic(s) in %s:\n", len(diags), rel)
for _, d := range diags {
sev := severityName[d.Severity]
if sev == "" {
sev = "error"
}
src := ""
if d.Source != "" {
src = " [" + d.Source + "]"
}
fmt.Fprintf(&b, "%d:%d %s%s %s\n",
d.Range.Start.Line+1, d.Range.Start.Character+1, sev, src,
strings.TrimSpace(d.Message))
}
return strings.TrimRight(b.String(), "\n")
}