parseLSPInvocation accepts the JSON envelope {cmd, args} or the argv form ("diagnostics " / "definition ").
(args []string)
| 245 | // parseLSPInvocation accepts the JSON envelope {cmd, args} or the argv form |
| 246 | // ("diagnostics <file>" / "definition <file> <line> <column>"). |
| 247 | func parseLSPInvocation(args []string) (string, string, error) { |
| 248 | payload := strings.TrimSpace(strings.Join(args, " ")) |
| 249 | |
| 250 | if strings.HasPrefix(payload, "{") { |
| 251 | var raw map[string]json.RawMessage |
| 252 | if err := json.Unmarshal([]byte(payload), &raw); err != nil { |
| 253 | return "", "", fmt.Errorf( |
| 254 | `parse envelope: %w. Expected {"cmd":"diagnostics","args":{"file":"..."}}`, err, |
| 255 | ) |
| 256 | } |
| 257 | var cmdStr string |
| 258 | if rc, ok := raw["cmd"]; ok { |
| 259 | _ = json.Unmarshal(rc, &cmdStr) |
| 260 | } |
| 261 | canon := canonicalLSPCmd(cmdStr) |
| 262 | if canon == "" { |
| 263 | return "", "", fmt.Errorf("missing or unknown cmd %q (valid: diagnostics|definition|references|symbols|hover)", cmdStr) |
| 264 | } |
| 265 | var inner string |
| 266 | if rargs, ok := raw["args"]; ok && len(rargs) > 0 { |
| 267 | inner = string(rargs) |
| 268 | } else { |
| 269 | delete(raw, "cmd") |
| 270 | b, _ := json.Marshal(raw) |
| 271 | inner = string(b) |
| 272 | } |
| 273 | return canon, inner, nil |
| 274 | } |
| 275 | |
| 276 | canon := canonicalLSPCmd(args[0]) |
| 277 | if canon == "" { |
| 278 | return "", "", fmt.Errorf("unknown cmd %q (valid: diagnostics|definition|references|symbols|hover)", args[0]) |
| 279 | } |
| 280 | // argv form: the agent flattener delivers the {cmd,args} envelope as |
| 281 | // "--flag value" pairs; the legacy positional "file [line column]" form |
| 282 | // stays supported when no flag token is present. |
| 283 | tail := args[1:] |
| 284 | if !hasFlagToken(tail) { |
| 285 | in := map[string]interface{}{} |
| 286 | if len(tail) > 0 { |
| 287 | in["file"] = tail[0] |
| 288 | } |
| 289 | if len(tail) > 2 { |
| 290 | in["line"] = atoiSafe(tail[1]) |
| 291 | in["column"] = atoiSafe(tail[2]) |
| 292 | } |
| 293 | b, _ := json.Marshal(in) |
| 294 | return canon, string(b), nil |
| 295 | } |
| 296 | return canon, argvInner(tail, "file", nil, map[string]bool{"line": true, "column": true, "limit": true}), nil |
| 297 | } |
| 298 | |
| 299 | // hasFlagToken reports whether any argv token is flag-shaped. |
| 300 | func hasFlagToken(args []string) bool { |