@@ -30,43 +30,55 @@ var (
30
30
// a copy of initialisms pre-baked as []rune
31
31
initialismsRunes [][]rune
32
32
initialismsUpperCased [][]rune
33
+ initialismsPluralForm []pluralForm // pre-baked indexed support for pluralization
33
34
34
35
isInitialism func (string ) bool
35
36
36
37
maxAllocMatches int
37
38
)
38
39
39
40
func init () {
40
- // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
41
+ // List of initialisms taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
42
+ //
43
+ // Now superseded by: https://github.com/mgechev/revive/blob/master/lint/name.go#L93
44
+ //
45
+ // Notice that initialisms are not necessarily uppercased.
46
+ // In particular, we may find plural forms with mixed case like "IDs" or legit values like "IPv4" or "IPv6".
47
+ //
48
+ // At this moment, we don't support pluralization of terms that ends with an 's' (or 'S').
49
+ // We don't want to support pluralization of terms which would otherwise conflict with another one,
50
+ // like "HTTPs" vs "HTTPS". All these should be considered invariant. Hence: "Https" matches "HTTPS" and
51
+ // "HTTPSS" is "HTTPS" followed by "S".
41
52
configuredInitialisms := map [string ]bool {
53
+ // initialism: true|false = accept a pluralized form 'Xs' - false means invariant plural
42
54
"ACL" : true ,
43
55
"API" : true ,
44
56
"ASCII" : true ,
45
57
"CPU" : true ,
46
- "CSS" : true ,
47
- "DNS" : true ,
58
+ "CSS" : false ,
59
+ "DNS" : false ,
48
60
"EOF" : true ,
49
61
"GUID" : true ,
50
62
"HTML" : true ,
51
- "HTTPS" : true ,
52
- "HTTP" : true ,
63
+ "HTTPS" : false ,
64
+ "HTTP" : false ,
53
65
"ID" : true ,
54
66
"IP" : true ,
55
- "IPv4" : true ,
56
- "IPv6" : true ,
67
+ "IPv4" : true , // prefer the mixed case outcome IPv4 over the capitalized IPV4
68
+ "IPv6" : true , // prefer the mixed case outcome
57
69
"JSON" : true ,
58
70
"LHS" : true ,
59
- "OAI" : true ,
60
- "QPS" : true ,
71
+ "OAI" : true , // not in the linter's list, but added for the openapi context
72
+ "QPS" : false ,
61
73
"RAM" : true ,
62
- "RHS" : true ,
74
+ "RHS" : false ,
63
75
"RPC" : true ,
64
76
"SLA" : true ,
65
77
"SMTP" : true ,
66
78
"SQL" : true ,
67
79
"SSH" : true ,
68
80
"TCP" : true ,
69
- "TLS" : true ,
81
+ "TLS" : false ,
70
82
"TTL" : true ,
71
83
"UDP" : true ,
72
84
"UI" : true ,
@@ -79,7 +91,7 @@ func init() {
79
91
"XML" : true ,
80
92
"XMPP" : true ,
81
93
"XSRF" : true ,
82
- "XSS" : true ,
94
+ "XSS" : false ,
83
95
}
84
96
85
97
// a thread-safe index of initialisms
@@ -88,6 +100,7 @@ func init() {
88
100
initialismsRunes = asRunes (initialisms )
89
101
initialismsUpperCased = asUpperCased (initialisms )
90
102
maxAllocMatches = maxAllocHeuristic (initialismsRunes )
103
+ initialismsPluralForm = asPluralForms (initialisms , commonInitialisms )
91
104
92
105
// a test function
93
106
isInitialism = commonInitialisms .isInitialism
@@ -112,6 +125,16 @@ func asUpperCased(in []string) [][]rune {
112
125
return out
113
126
}
114
127
128
+ // asPluralForms bakes an index of pluralization support.
129
+ func asPluralForms (in []string , idx * indexOfInitialisms ) []pluralForm {
130
+ out := make ([]pluralForm , len (in ))
131
+ for i , initialism := range in {
132
+ out [i ] = idx .pluralForm (initialism )
133
+ }
134
+
135
+ return out
136
+ }
137
+
115
138
func maxAllocHeuristic (in [][]rune ) int {
116
139
heuristic := make (map [rune ]int )
117
140
for _ , initialism := range in {
@@ -139,12 +162,14 @@ func maxAllocHeuristic(in [][]rune) int {
139
162
func AddInitialisms (words ... string ) {
140
163
for _ , word := range words {
141
164
// commonInitialisms[upper(word)] = true
142
- commonInitialisms .add (upper (word ))
165
+ uword := upper (word )
166
+ commonInitialisms .add (uword , ! strings .HasSuffix (uword , "S" ))
143
167
}
144
168
// sort again
145
169
initialisms = commonInitialisms .sorted ()
146
170
initialismsRunes = asRunes (initialisms )
147
171
initialismsUpperCased = asUpperCased (initialisms )
172
+ initialismsPluralForm = asPluralForms (initialisms , commonInitialisms )
148
173
}
149
174
150
175
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
@@ -175,8 +200,8 @@ func (m *indexOfInitialisms) isInitialism(key string) bool {
175
200
return ok
176
201
}
177
202
178
- func (m * indexOfInitialisms ) add (key string ) * indexOfInitialisms {
179
- m .index .Store (key , true )
203
+ func (m * indexOfInitialisms ) add (key string , hasPlural bool ) * indexOfInitialisms {
204
+ m .index .Store (key , hasPlural )
180
205
return m
181
206
}
182
207
@@ -192,6 +217,40 @@ func (m *indexOfInitialisms) sorted() (result []string) {
192
217
return
193
218
}
194
219
220
+ // pluralForm denotes the kind of pluralization to be used for initialisms.
221
+ //
222
+ // At this moment, initialisms are either invariant or follow a simple plural form with an
223
+ // extra (lower case) "s".
224
+ type pluralForm uint8
225
+
226
+ const (
227
+ notPlural pluralForm = iota
228
+ invariantPlural
229
+ simplePlural
230
+ )
231
+
232
+ // pluralForm indicates how we want to pluralize a given initialism.
233
+ //
234
+ // Besides configured invariant forms (like HTTP and HTTPS),
235
+ // an initialism is normally pluralized by adding a single 's', like in IDs.
236
+ //
237
+ // Initialisms ending with an 'S' or an 's' are configured as invariant (we don't
238
+ // support plural forms like CSSes or DNSes, however the mechanism could be extended to
239
+ // do just that).
240
+ func (m * indexOfInitialisms ) pluralForm (key string ) pluralForm {
241
+ v , ok := m .index .Load (key )
242
+ if ! ok {
243
+ return notPlural
244
+ }
245
+
246
+ acceptsPlural := v .(bool )
247
+ if ! acceptsPlural {
248
+ return invariantPlural
249
+ }
250
+
251
+ return simplePlural
252
+ }
253
+
195
254
type byInitialism []string
196
255
197
256
func (s byInitialism ) Len () int {
@@ -200,10 +259,14 @@ func (s byInitialism) Len() int {
200
259
func (s byInitialism ) Swap (i , j int ) {
201
260
s [i ], s [j ] = s [j ], s [i ]
202
261
}
262
+
263
+ // Less specifies the order in which initialisms are prioritized:
264
+ // 1. match longest first
265
+ // 2. when equal length, match in reverse lexicographical order, lower case match comes first
203
266
func (s byInitialism ) Less (i , j int ) bool {
204
267
if len (s [i ]) != len (s [j ]) {
205
268
return len (s [i ]) < len (s [j ])
206
269
}
207
270
208
- return strings . Compare ( s [i ], s [j ]) > 0
271
+ return s [i ] < s [j ]
209
272
}
0 commit comments