blob: 237d19e4f06283f9915cdcdaff608e78bc1359da [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.19
// +build go1.19
package main
import (
"fmt"
"log"
"strings"
)
var typeNames = make(map[*Type]string)
var genTypes = make(map[*Type]*newType)
func findTypeNames(model Model) {
for _, s := range model.Structures {
for _, e := range s.Extends {
nameType(e, nil) // all references
}
for _, m := range s.Mixins {
nameType(m, nil) // all references
}
for _, p := range s.Properties {
nameType(p.Type, []string{s.Name, p.Name})
}
}
for _, t := range model.Enumerations {
nameType(t.Type, []string{t.Name})
}
for _, t := range model.TypeAliases {
nameType(t.Type, []string{t.Name})
}
for _, r := range model.Requests {
nameType(r.Params, []string{"Param", r.Method})
nameType(r.Result, []string{"Result", r.Method})
nameType(r.RegistrationOptions, []string{"RegOpt", r.Method})
}
for _, n := range model.Notifications {
nameType(n.Params, []string{"Param", n.Method})
nameType(n.RegistrationOptions, []string{"RegOpt", n.Method})
}
}
// nameType populates typeNames[t] with the computed name of the type.
// path is the list of enclosing constructs in the JSON model.
func nameType(t *Type, path []string) string {
if t == nil || typeNames[t] != "" {
return ""
}
switch t.Kind {
case "base":
typeNames[t] = t.Name
return t.Name
case "reference":
typeNames[t] = t.Name
return t.Name
case "array":
nm := "[]" + nameType(t.Element, append(path, "Elem"))
typeNames[t] = nm
return nm
case "map":
key := nameType(t.Key, nil) // never a generated type
value := nameType(t.Value.(*Type), append(path, "Value"))
nm := "map[" + key + "]" + value
typeNames[t] = nm
return nm
// generated types
case "and":
nm := nameFromPath("And", path)
typeNames[t] = nm
for _, it := range t.Items {
nameType(it, append(path, "Item"))
}
genTypes[t] = &newType{
name: nm,
typ: t,
kind: "and",
items: t.Items,
line: t.Line,
}
return nm
case "literal":
nm := nameFromPath("Lit", path)
typeNames[t] = nm
for _, p := range t.Value.(ParseLiteral).Properties {
nameType(p.Type, append(path, p.Name))
}
genTypes[t] = &newType{
name: nm,
typ: t,
kind: "literal",
properties: t.Value.(ParseLiteral).Properties,
line: t.Line,
}
return nm
case "tuple":
nm := nameFromPath("Tuple", path)
typeNames[t] = nm
for _, it := range t.Items {
nameType(it, append(path, "Item"))
}
genTypes[t] = &newType{
name: nm,
typ: t,
kind: "tuple",
items: t.Items,
line: t.Line,
}
return nm
case "or":
nm := nameFromPath("Or", path)
typeNames[t] = nm
for i, it := range t.Items {
// these names depend on the ordering within the "or" type
nameType(it, append(path, fmt.Sprintf("Item%d", i)))
}
// this code handles an "or" of stringLiterals (_InitializeParams.trace)
names := make(map[string]int)
msg := ""
for _, it := range t.Items {
if line, ok := names[typeNames[it]]; ok {
// duplicate component names are bad
msg += fmt.Sprintf("lines %d %d dup, %s for %s\n", line, it.Line, typeNames[it], nm)
}
names[typeNames[it]] = t.Line
}
// this code handles an "or" of stringLiterals (_InitializeParams.trace)
if len(names) == 1 {
var solekey string
for k := range names {
solekey = k // the sole name
}
if solekey == "string" { // _InitializeParams.trace
typeNames[t] = "string"
return "string"
}
// otherwise unexpected
log.Printf("unexpected: single-case 'or' type has non-string key %s: %s", nm, solekey)
log.Fatal(msg)
} else if len(names) == 2 {
// if one of the names is null, just use the other, rather than generating an "or".
// This removes about 40 types from the generated code. An entry in goplsStar
// could be added to handle the null case, if necessary.
newNm := ""
sawNull := false
for k := range names {
if k == "null" {
sawNull = true
} else {
newNm = k
}
}
if sawNull {
typeNames[t] = newNm
return newNm
}
}
genTypes[t] = &newType{
name: nm,
typ: t,
kind: "or",
items: t.Items,
line: t.Line,
}
return nm
case "stringLiteral": // a single type, like 'kind' or 'rename'
typeNames[t] = "string"
return "string"
default:
log.Fatalf("nameType: %T unexpected, line:%d path:%v", t, t.Line, path)
panic("unreachable in nameType")
}
}
func nameFromPath(prefix string, path []string) string {
nm := prefix + "_" + strings.Join(path, "_")
// methods have slashes
nm = strings.ReplaceAll(nm, "/", "_")
return nm
}