golang.org/x/tools/gopls@v0.15.3/internal/protocol/generate/output.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.19 6 // +build go1.19 7 8 package main 9 10 import ( 11 "bytes" 12 "fmt" 13 "log" 14 "sort" 15 "strings" 16 ) 17 18 var ( 19 // tsclient.go has 3 sections 20 cdecls = make(sortedMap[string]) 21 ccases = make(sortedMap[string]) 22 cfuncs = make(sortedMap[string]) 23 // tsserver.go has 3 sections 24 sdecls = make(sortedMap[string]) 25 scases = make(sortedMap[string]) 26 sfuncs = make(sortedMap[string]) 27 // tsprotocol.go has 2 sections 28 types = make(sortedMap[string]) 29 consts = make(sortedMap[string]) 30 // tsjson has 1 section 31 jsons = make(sortedMap[string]) 32 ) 33 34 func generateOutput(model Model) { 35 for _, r := range model.Requests { 36 genDecl(r.Method, r.Params, r.Result, r.Direction) 37 genCase(r.Method, r.Params, r.Result, r.Direction) 38 genFunc(r.Method, r.Params, r.Result, r.Direction, false) 39 } 40 for _, n := range model.Notifications { 41 if n.Method == "$/cancelRequest" { 42 continue // handled internally by jsonrpc2 43 } 44 genDecl(n.Method, n.Params, nil, n.Direction) 45 genCase(n.Method, n.Params, nil, n.Direction) 46 genFunc(n.Method, n.Params, nil, n.Direction, true) 47 } 48 genStructs(model) 49 genAliases(model) 50 genGenTypes() // generate the unnamed types 51 genConsts(model) 52 genMarshal() 53 } 54 55 func genDecl(method string, param, result *Type, dir string) { 56 fname := methodName(method) 57 p := "" 58 if notNil(param) { 59 p = ", *" + goplsName(param) 60 } 61 ret := "error" 62 if notNil(result) { 63 tp := goplsName(result) 64 if !hasNilValue(tp) { 65 tp = "*" + tp 66 } 67 ret = fmt.Sprintf("(%s, error)", tp) 68 } 69 // special gopls compatibility case (PJW: still needed?) 70 switch method { 71 case "workspace/configuration": 72 // was And_Param_workspace_configuration, but the type substitution doesn't work, 73 // as ParamConfiguration is embedded in And_Param_workspace_configuration 74 p = ", *ParamConfiguration" 75 ret = "([]LSPAny, error)" 76 } 77 msg := fmt.Sprintf("\t%s(context.Context%s) %s // %s\n", fname, p, ret, method) 78 switch dir { 79 case "clientToServer": 80 sdecls[method] = msg 81 case "serverToClient": 82 cdecls[method] = msg 83 case "both": 84 sdecls[method] = msg 85 cdecls[method] = msg 86 default: 87 log.Fatalf("impossible direction %q", dir) 88 } 89 } 90 91 func genCase(method string, param, result *Type, dir string) { 92 out := new(bytes.Buffer) 93 fmt.Fprintf(out, "\tcase %q:\n", method) 94 var p string 95 fname := methodName(method) 96 if notNil(param) { 97 nm := goplsName(param) 98 if method == "workspace/configuration" { // gopls compatibility 99 // was And_Param_workspace_configuration, which contains ParamConfiguration 100 // so renaming the type leads to circular definitions 101 nm = "ParamConfiguration" // gopls compatibility 102 } 103 fmt.Fprintf(out, "\t\tvar params %s\n", nm) 104 fmt.Fprintf(out, "\t\tif err := UnmarshalJSON(r.Params(), ¶ms); err != nil {\n") 105 fmt.Fprintf(out, "\t\t\treturn true, sendParseError(ctx, reply, err)\n\t\t}\n") 106 p = ", ¶ms" 107 } 108 if notNil(result) { 109 fmt.Fprintf(out, "\t\tresp, err := %%s.%s(ctx%s)\n", fname, p) 110 out.WriteString("\t\tif err != nil {\n") 111 out.WriteString("\t\t\treturn true, reply(ctx, nil, err)\n") 112 out.WriteString("\t\t}\n") 113 out.WriteString("\t\treturn true, reply(ctx, resp, nil)\n") 114 } else { 115 fmt.Fprintf(out, "\t\terr := %%s.%s(ctx%s)\n", fname, p) 116 out.WriteString("\t\treturn true, reply(ctx, nil, err)\n") 117 } 118 out.WriteString("\n") 119 msg := out.String() 120 switch dir { 121 case "clientToServer": 122 scases[method] = fmt.Sprintf(msg, "server") 123 case "serverToClient": 124 ccases[method] = fmt.Sprintf(msg, "client") 125 case "both": 126 scases[method] = fmt.Sprintf(msg, "server") 127 ccases[method] = fmt.Sprintf(msg, "client") 128 default: 129 log.Fatalf("impossible direction %q", dir) 130 } 131 } 132 133 func genFunc(method string, param, result *Type, dir string, isnotify bool) { 134 out := new(bytes.Buffer) 135 var p, r string 136 var goResult string 137 if notNil(param) { 138 p = ", params *" + goplsName(param) 139 } 140 if notNil(result) { 141 goResult = goplsName(result) 142 if !hasNilValue(goResult) { 143 goResult = "*" + goResult 144 } 145 r = fmt.Sprintf("(%s, error)", goResult) 146 } else { 147 r = "error" 148 } 149 // special gopls compatibility case 150 switch method { 151 case "workspace/configuration": 152 // was And_Param_workspace_configuration, but the type substitution doesn't work, 153 // as ParamConfiguration is embedded in And_Param_workspace_configuration 154 p = ", params *ParamConfiguration" 155 r = "([]LSPAny, error)" 156 goResult = "[]LSPAny" 157 } 158 fname := methodName(method) 159 fmt.Fprintf(out, "func (s *%%sDispatcher) %s(ctx context.Context%s) %s {\n", 160 fname, p, r) 161 162 if !notNil(result) { 163 if isnotify { 164 if notNil(param) { 165 fmt.Fprintf(out, "\treturn s.sender.Notify(ctx, %q, params)\n", method) 166 } else { 167 fmt.Fprintf(out, "\treturn s.sender.Notify(ctx, %q, nil)\n", method) 168 } 169 } else { 170 if notNil(param) { 171 fmt.Fprintf(out, "\treturn s.sender.Call(ctx, %q, params, nil)\n", method) 172 } else { 173 fmt.Fprintf(out, "\treturn s.sender.Call(ctx, %q, nil, nil)\n", method) 174 } 175 } 176 } else { 177 fmt.Fprintf(out, "\tvar result %s\n", goResult) 178 if isnotify { 179 if notNil(param) { 180 fmt.Fprintf(out, "\ts.sender.Notify(ctx, %q, params)\n", method) 181 } else { 182 fmt.Fprintf(out, "\t\tif err := s.sender.Notify(ctx, %q, nil); err != nil {\n", method) 183 } 184 } else { 185 if notNil(param) { 186 fmt.Fprintf(out, "\t\tif err := s.sender.Call(ctx, %q, params, &result); err != nil {\n", method) 187 } else { 188 fmt.Fprintf(out, "\t\tif err := s.sender.Call(ctx, %q, nil, &result); err != nil {\n", method) 189 } 190 } 191 fmt.Fprintf(out, "\t\treturn nil, err\n\t}\n\treturn result, nil\n") 192 } 193 out.WriteString("}\n") 194 msg := out.String() 195 switch dir { 196 case "clientToServer": 197 sfuncs[method] = fmt.Sprintf(msg, "server") 198 case "serverToClient": 199 cfuncs[method] = fmt.Sprintf(msg, "client") 200 case "both": 201 sfuncs[method] = fmt.Sprintf(msg, "server") 202 cfuncs[method] = fmt.Sprintf(msg, "client") 203 default: 204 log.Fatalf("impossible direction %q", dir) 205 } 206 } 207 208 func genStructs(model Model) { 209 structures := make(map[string]*Structure) // for expanding Extends 210 for _, s := range model.Structures { 211 structures[s.Name] = s 212 } 213 for _, s := range model.Structures { 214 out := new(bytes.Buffer) 215 generateDoc(out, s.Documentation) 216 nm := goName(s.Name) 217 if nm == "string" { // an unacceptable strut name 218 // a weird case, and needed only so the generated code contains the old gopls code 219 nm = "DocumentDiagnosticParams" 220 } 221 fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(s.Line)) 222 // for gpls compatibilitye, embed most extensions, but expand the rest some day 223 props := append([]NameType{}, s.Properties...) 224 if s.Name == "SymbolInformation" { // but expand this one 225 for _, ex := range s.Extends { 226 fmt.Fprintf(out, "\t// extends %s\n", ex.Name) 227 props = append(props, structures[ex.Name].Properties...) 228 } 229 genProps(out, props, nm) 230 } else { 231 genProps(out, props, nm) 232 for _, ex := range s.Extends { 233 fmt.Fprintf(out, "\t%s\n", goName(ex.Name)) 234 } 235 } 236 for _, ex := range s.Mixins { 237 fmt.Fprintf(out, "\t%s\n", goName(ex.Name)) 238 } 239 out.WriteString("}\n") 240 types[nm] = out.String() 241 } 242 243 // base types 244 // (For URI and DocumentURI, see ../uri.go.) 245 types["LSPAny"] = "type LSPAny = interface{}\n" 246 // A special case, the only previously existing Or type 247 types["DocumentDiagnosticReport"] = "type DocumentDiagnosticReport = Or_DocumentDiagnosticReport // (alias) \n" 248 249 } 250 251 func genProps(out *bytes.Buffer, props []NameType, name string) { 252 for _, p := range props { 253 tp := goplsName(p.Type) 254 if newNm, ok := renameProp[prop{name, p.Name}]; ok { 255 usedRenameProp[prop{name, p.Name}] = true 256 if tp == newNm { 257 log.Printf("renameProp useless {%q, %q} for %s", name, p.Name, tp) 258 } 259 tp = newNm 260 } 261 // it's a pointer if it is optional, or for gopls compatibility 262 opt, star := propStar(name, p, tp) 263 json := fmt.Sprintf(" `json:\"%s%s\"`", p.Name, opt) 264 generateDoc(out, p.Documentation) 265 fmt.Fprintf(out, "\t%s %s%s %s\n", goName(p.Name), star, tp, json) 266 } 267 } 268 269 func genAliases(model Model) { 270 for _, ta := range model.TypeAliases { 271 out := new(bytes.Buffer) 272 generateDoc(out, ta.Documentation) 273 nm := goName(ta.Name) 274 if nm != ta.Name { 275 continue // renamed the type, e.g., "DocumentDiagnosticReport", an or-type to "string" 276 } 277 tp := goplsName(ta.Type) 278 fmt.Fprintf(out, "type %s = %s // (alias)\n", nm, tp) 279 types[nm] = out.String() 280 } 281 } 282 283 func genGenTypes() { 284 for _, nt := range genTypes { 285 out := new(bytes.Buffer) 286 nm := goplsName(nt.typ) 287 switch nt.kind { 288 case "literal": 289 fmt.Fprintf(out, "// created for Literal (%s)\n", nt.name) 290 fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) 291 genProps(out, nt.properties, nt.name) // systematic name, not gopls name; is this a good choice? 292 case "or": 293 if !strings.HasPrefix(nm, "Or") { 294 // It was replaced by a narrower type defined elsewhere 295 continue 296 } 297 names := []string{} 298 for _, t := range nt.items { 299 if notNil(t) { 300 names = append(names, goplsName(t)) 301 } 302 } 303 sort.Strings(names) 304 fmt.Fprintf(out, "// created for Or %v\n", names) 305 fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) 306 fmt.Fprintf(out, "\tValue interface{} `json:\"value\"`\n") 307 case "and": 308 fmt.Fprintf(out, "// created for And\n") 309 fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) 310 for _, x := range nt.items { 311 nm := goplsName(x) 312 fmt.Fprintf(out, "\t%s\n", nm) 313 } 314 case "tuple": // there's only this one 315 nt.name = "UIntCommaUInt" 316 fmt.Fprintf(out, "//created for Tuple\ntype %s struct {%s\n", nm, linex(nt.line+1)) 317 fmt.Fprintf(out, "\tFld0 uint32 `json:\"fld0\"`\n") 318 fmt.Fprintf(out, "\tFld1 uint32 `json:\"fld1\"`\n") 319 default: 320 log.Fatalf("%s not handled", nt.kind) 321 } 322 out.WriteString("}\n") 323 types[nm] = out.String() 324 } 325 } 326 func genConsts(model Model) { 327 for _, e := range model.Enumerations { 328 out := new(bytes.Buffer) 329 generateDoc(out, e.Documentation) 330 tp := goplsName(e.Type) 331 nm := goName(e.Name) 332 fmt.Fprintf(out, "type %s %s%s\n", nm, tp, linex(e.Line)) 333 types[nm] = out.String() 334 vals := new(bytes.Buffer) 335 generateDoc(vals, e.Documentation) 336 for _, v := range e.Values { 337 generateDoc(vals, v.Documentation) 338 nm := goName(v.Name) 339 more, ok := disambiguate[e.Name] 340 if ok { 341 usedDisambiguate[e.Name] = true 342 nm = more.prefix + nm + more.suffix 343 nm = goName(nm) // stringType 344 } 345 var val string 346 switch v := v.Value.(type) { 347 case string: 348 val = fmt.Sprintf("%q", v) 349 case float64: 350 val = fmt.Sprintf("%d", int(v)) 351 default: 352 log.Fatalf("impossible type %T", v) 353 } 354 fmt.Fprintf(vals, "\t%s %s = %s%s\n", nm, e.Name, val, linex(v.Line)) 355 } 356 consts[nm] = vals.String() 357 } 358 } 359 func genMarshal() { 360 for _, nt := range genTypes { 361 nm := goplsName(nt.typ) 362 if !strings.HasPrefix(nm, "Or") { 363 continue 364 } 365 names := []string{} 366 for _, t := range nt.items { 367 if notNil(t) { 368 names = append(names, goplsName(t)) 369 } 370 } 371 sort.Strings(names) 372 var buf bytes.Buffer 373 fmt.Fprintf(&buf, "func (t %s) MarshalJSON() ([]byte, error) {\n", nm) 374 buf.WriteString("\tswitch x := t.Value.(type){\n") 375 for _, nmx := range names { 376 fmt.Fprintf(&buf, "\tcase %s:\n", nmx) 377 fmt.Fprintf(&buf, "\t\treturn json.Marshal(x)\n") 378 } 379 buf.WriteString("\tcase nil:\n\t\treturn []byte(\"null\"), nil\n\t}\n") 380 fmt.Fprintf(&buf, "\treturn nil, fmt.Errorf(\"type %%T not one of %v\", t)\n", names) 381 buf.WriteString("}\n\n") 382 383 fmt.Fprintf(&buf, "func (t *%s) UnmarshalJSON(x []byte) error {\n", nm) 384 buf.WriteString("\tif string(x) == \"null\" {\n\t\tt.Value = nil\n\t\t\treturn nil\n\t}\n") 385 for i, nmx := range names { 386 fmt.Fprintf(&buf, "\tvar h%d %s\n", i, nmx) 387 fmt.Fprintf(&buf, "\tif err := json.Unmarshal(x, &h%d); err == nil {\n\t\tt.Value = h%d\n\t\t\treturn nil\n\t\t}\n", i, i) 388 } 389 fmt.Fprintf(&buf, "return &UnmarshalError{\"unmarshal failed to match one of %v\"}", names) 390 buf.WriteString("}\n\n") 391 jsons[nm] = buf.String() 392 } 393 } 394 395 func linex(n int) string { 396 if *lineNumbers { 397 return fmt.Sprintf(" // line %d", n) 398 } 399 return "" 400 } 401 402 func goplsName(t *Type) string { 403 nm := typeNames[t] 404 // translate systematic name to gopls name 405 if newNm, ok := goplsType[nm]; ok { 406 usedGoplsType[nm] = true 407 nm = newNm 408 } 409 return nm 410 } 411 412 func notNil(t *Type) bool { // shutdwon is the special case that needs this 413 return t != nil && (t.Kind != "base" || t.Name != "null") 414 } 415 416 func hasNilValue(t string) bool { 417 // this may be unreliable, and need a supplementary table 418 if strings.HasPrefix(t, "[]") || strings.HasPrefix(t, "*") { 419 return true 420 } 421 if t == "interface{}" || t == "any" { 422 return true 423 } 424 // that's all the cases that occur currently 425 return false 426 }