aboutsummaryrefslogtreecommitdiff
path: root/gopls/internal/lsp/protocol/generate/generate.go
blob: 0496b7d060ce5187da3dc6a9a68082e349bdab38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// 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 (
	"bytes"
	"fmt"
	"log"
	"strings"
)

// a newType is a type that needs a name and a definition
// These are the various types that the json specification doesn't name
type newType struct {
	name       string
	properties Properties // for struct/literal types
	items      []*Type    // for other types ("and", "tuple")
	line       int
	kind       string // Or, And, Tuple, Lit, Map
	typ        *Type
}

func generateDoc(out *bytes.Buffer, doc string) {
	if doc == "" {
		return
	}

	if !strings.Contains(doc, "\n") {
		fmt.Fprintf(out, "// %s\n", doc)
		return
	}
	var list bool
	for _, line := range strings.Split(doc, "\n") {
		// Lists in metaModel.json start with a dash.
		// To make a go doc list they have to be preceded
		// by a blank line, and indented.
		// (see type TextDccumentFilter in protocol.go)
		if len(line) > 0 && line[0] == '-' {
			if !list {
				list = true
				fmt.Fprintf(out, "//\n")
			}
			fmt.Fprintf(out, "//  %s\n", line)
		} else {
			if len(line) == 0 {
				list = false
			}
			fmt.Fprintf(out, "// %s\n", line)
		}
	}
}

// decide if a property is optional, and if it needs a *
// return ",omitempty" if it is optional, and "*" if it needs a pointer
func propStar(name string, t NameType, gotype string) (string, string) {
	var opt, star string
	if t.Optional {
		star = "*"
		opt = ",omitempty"
	}
	if strings.HasPrefix(gotype, "[]") || strings.HasPrefix(gotype, "map[") {
		star = "" // passed by reference, so no need for *
	} else {
		switch gotype {
		case "bool", "uint32", "int32", "string", "interface{}":
			star = "" // gopls compatibility if t.Optional
		}
	}
	ostar, oopt := star, opt
	if newStar, ok := goplsStar[prop{name, t.Name}]; ok {
		switch newStar {
		case nothing:
			star, opt = "", ""
		case wantStar:
			star, opt = "*", ""
		case wantOpt:
			star, opt = "", ",omitempty"
		case wantOptStar:
			star, opt = "*", ",omitempty"
		}
		if star == ostar && opt == oopt { // no change
			log.Printf("goplsStar[ {%q, %q} ](%d) useless %s/%s %s/%s", name, t.Name, t.Line, ostar, star, oopt, opt)
		}
		usedGoplsStar[prop{name, t.Name}] = true
	}

	return opt, star
}

func goName(s string) string {
	// Go naming conventions
	if strings.HasSuffix(s, "Id") {
		s = s[:len(s)-len("Id")] + "ID"
	} else if strings.HasSuffix(s, "Uri") {
		s = s[:len(s)-3] + "URI"
	} else if s == "uri" {
		s = "URI"
	} else if s == "id" {
		s = "ID"
	}

	// renames for temporary GOPLS compatibility
	if news := goplsType[s]; news != "" {
		usedGoplsType[s] = true
		s = news
	}
	// Names beginning _ are not exported
	if strings.HasPrefix(s, "_") {
		s = strings.Replace(s, "_", "X", 1)
	}
	if s != "string" { // base types are unchanged (textDocuemnt/diagnostic)
		// Title is deprecated, but a) s is only one word, b) replacement is too heavy-weight
		s = strings.Title(s)
	}
	return s
}