Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / protocol / typescript / code.ts
1 // read files from vscode-languageserver-node, and generate Go rpc stubs
2 // and data definitions. (and maybe someday unmarshaling code)
3
4 // The output is 3 files, tsprotocol.go contains the type definitions
5 // while tsclient.go and tsserver.go contain the LSP API and stub. An LSP server
6 // uses both APIs. To read the code, start in this file's main() function.
7
8 // The code is rich in heuristics and special cases, some of which are to avoid
9 // extensive changes to gopls, and some of which are due to the mismatch between
10 // typescript and Go types. In particular, there is no Go equivalent to union
11 // types, so each case ought to be considered separately. The Go equivalent of A
12 // & B could frequently be struct{A;B;}, or it could be the equivalent type
13 // listing all the members of A and B. Typically the code uses the former, but
14 // especially if A and B have elements with the same name, it does a version of
15 // the latter. ClientCapabilities has to be expanded, and ServerCapabilities is
16 // expanded to make the generated code easier to read.
17
18 // for us typescript ignorati, having an import makes this file a module
19 import * as fs from 'fs';
20 import * as ts from 'typescript';
21 import * as u from './util';
22 import { constName, getComments, goName, loc, strKind } from './util';
23
24 var program: ts.Program;
25
26 function parse() {
27   // this won't complain if some fnames don't exist
28   program = ts.createProgram(
29     u.fnames,
30     { target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.CommonJS });
31   program.getTypeChecker();  // finish type checking and assignment
32 }
33
34 // ----- collecting information for RPCs
35 let req = new Map<string, ts.NewExpression>();               // requests
36 let not = new Map<string, ts.NewExpression>();               // notifications
37 let ptypes = new Map<string, [ts.TypeNode, ts.TypeNode]>();  // req, resp types
38 let receives = new Map<string, 'server' | 'client'>();         // who receives it
39 let rpcTypes = new Set<string>();  // types seen in the rpcs
40
41 function findRPCs(node: ts.Node) {
42   if (!ts.isModuleDeclaration(node)) {
43     return
44   }
45   if (!ts.isIdentifier(node.name)) {
46     throw new Error(
47       `expected Identifier, got ${strKind(node.name)} at ${loc(node)}`)
48   }
49   let reqnot = req
50   let v = node.name.getText()
51   if (v.endsWith('Notification')) reqnot = not;
52   else if (!v.endsWith('Request')) return;
53
54   if (!ts.isModuleBlock(node.body)) {
55     throw new Error(
56       `expected ModuleBody got ${strKind(node.body)} at ${loc(node)}`)
57   }
58   let x: ts.ModuleBlock = node.body
59   // The story is to expect const method = 'textDocument/implementation'
60   // const type = new ProtocolRequestType<...>(method)
61   // but the method may be an explicit string
62   let rpc: string = '';
63   let newNode: ts.NewExpression;
64   for (let i = 0; i < x.statements.length; i++) {
65     const uu = x.statements[i];
66     if (!ts.isVariableStatement(uu)) continue;
67     const dl: ts.VariableDeclarationList = uu.declarationList;
68     if (dl.declarations.length != 1)
69       throw new Error(`expected a single decl at ${loc(dl)}`);
70     const decl: ts.VariableDeclaration = dl.declarations[0];
71     const name = decl.name.getText()
72     // we want the initializers
73     if (name == 'method') {  // mostly StringLiteral but NoSubstitutionTemplateLiteral in protocol.semanticTokens.ts
74       if (!ts.isStringLiteral(decl.initializer)) {
75         if (!ts.isNoSubstitutionTemplateLiteral(decl.initializer)) {
76           console.log(`${decl.initializer.getText()}`);
77           throw new Error(`expect StringLiteral at ${loc(decl)} got ${strKind(decl.initializer)}`);
78         }
79       }
80       rpc = decl.initializer.getText()
81     }
82     else if (name == 'type') {  // NewExpression
83       if (!ts.isNewExpression(decl.initializer))
84         throw new Error(`expecte new at ${loc(decl)}`);
85       const nn: ts.NewExpression = decl.initializer
86       newNode = nn
87       const mtd = nn.arguments[0];
88       if (ts.isStringLiteral(mtd)) rpc = mtd.getText();
89       switch (nn.typeArguments.length) {
90         case 1:  // exit
91           ptypes.set(rpc, [nn.typeArguments[0], null])
92           break;
93         case 2:  // notifications
94           ptypes.set(rpc, [nn.typeArguments[0], null])
95           break;
96         case 4:  // request with no parameters
97           ptypes.set(rpc, [null, nn.typeArguments[0]])
98           break;
99         case 5:  // request req, resp, partial(?)
100           ptypes.set(rpc, [nn.typeArguments[0], nn.typeArguments[1]])
101           break;
102         default:
103           throw new Error(`${nn.typeArguments.length} at ${loc(nn)}`)
104       }
105     }
106   }
107   if (rpc == '') throw new Error(`no name found at ${loc(x)}`);
108   // remember the implied types
109   const [a, b] = ptypes.get(rpc);
110   const add = function (n: ts.Node) {
111     rpcTypes.add(goName(n.getText()))
112   };
113   underlying(a, add);
114   underlying(b, add);
115   rpc = rpc.substring(1, rpc.length - 1);  // 'exit'
116   reqnot.set(rpc, newNode)
117 }
118
119 // handle missing typeArguments
120 function lookUp(n: ts.NewExpression): ts.NodeArray<ts.TypeNode> {
121   // parent should be VariableDeclaration. its children should be
122   // Identifier('type') ???
123   // TypeReference: [Identifier('RequestType1), ]
124   // NewExpression (us)
125   const p = n.parent;
126   if (!ts.isVariableDeclaration(p)) throw new Error(`not variable decl`);
127   const tr = p.type;
128   if (!ts.isTypeReferenceNode(tr)) throw new Error(`not TypeReference`);
129   return tr.typeArguments;
130 }
131
132 function setReceives() {
133   // mark them all as server, then adjust the client ones.
134   // it would be nice to have some independent check on this
135   // (this logic fails if the server ever sends $/canceRequest
136   //  or $/progress)
137   req.forEach((_, k) => { receives.set(k, 'server') });
138   not.forEach((_, k) => { receives.set(k, 'server') });
139   receives.set('window/showMessage', 'client');
140   receives.set('window/showMessageRequest', 'client');
141   receives.set('window/logMessage', 'client');
142   receives.set('telemetry/event', 'client');
143   receives.set('client/registerCapability', 'client');
144   receives.set('client/unregisterCapability', 'client');
145   receives.set('workspace/workspaceFolders', 'client');
146   receives.set('workspace/configuration', 'client');
147   receives.set('workspace/applyEdit', 'client');
148   receives.set('textDocument/publishDiagnostics', 'client');
149   receives.set('window/workDoneProgress/create', 'client');
150   receives.set('$/progress', 'client');
151   // a small check
152   receives.forEach((_, k) => {
153     if (!req.get(k) && !not.get(k)) throw new Error(`missing ${k}}`);
154     if (req.get(k) && not.get(k)) throw new Error(`dup ${k}`);
155   })
156 }
157
158 interface Data {
159   me: ts.Node;   // root node for this type
160   name: string;  // Go name
161   generics: ts.NodeArray<ts.TypeParameterDeclaration>;
162   as: ts.NodeArray<ts.HeritageClause>;  // inheritance
163   // Interface
164   properties: ts.NodeArray<ts.TypeElement>;  // ts.PropertySignature
165   alias: ts.TypeNode;                        // type alias
166   // module
167   statements: ts.NodeArray<ts.Statement>;
168   enums: ts.NodeArray<ts.EnumMember>;
169   // class
170   members: ts.NodeArray<ts.PropertyDeclaration>;
171 }
172 function newData(n: ts.Node, nm: string): Data {
173   return {
174     me: n, name: goName(nm),
175     generics: ts.createNodeArray<ts.TypeParameterDeclaration>(), as: ts.createNodeArray<ts.HeritageClause>(),
176     properties: ts.createNodeArray<ts.TypeElement>(), alias: undefined,
177     statements: ts.createNodeArray<ts.Statement>(),
178     enums: ts.createNodeArray<ts.EnumMember>(),
179     members: ts.createNodeArray<ts.PropertyDeclaration>(),
180   }
181 }
182
183 // for debugging, produce a skeleton description
184 function strData(d: Data): string {
185   const f = function (na: ts.NodeArray<any>): number {
186     return na.length
187   };
188   return `D(${d.name}) g;${f(d.generics)} a:${f(d.as)} p:${f(d.properties)} s:${
189     f(d.statements)} e:${f(d.enums)} m:${f(d.members)} ${
190     d.alias != undefined}`
191 }
192
193 let data = new Map<string, Data>();            // parsed data types
194 let seenTypes = new Map<string, Data>();       // type names we've seen
195 let extraTypes = new Map<string, string[]>();  // to avoid struct params
196
197 // look at top level data definitions
198 function genTypes(node: ts.Node) {
199   // Ignore top-level items that can't produce output
200   if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
201     ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
202     ts.isExportDeclaration(node) || ts.isEmptyStatement(node) ||
203     ts.isExportAssignment(node) || ts.isImportEqualsDeclaration(node) ||
204     ts.isBlock(node) || node.kind == ts.SyntaxKind.EndOfFileToken) {
205     return;
206   }
207   if (ts.isInterfaceDeclaration(node)) {
208     const v: ts.InterfaceDeclaration = node;
209     // need to check the members, many of which are disruptive
210     let mems: ts.TypeElement[] = [];
211     const f = function (t: ts.TypeElement) {
212       if (ts.isPropertySignature(t)) {
213         mems.push(t);
214       } else if (ts.isMethodSignature(t) || ts.isCallSignatureDeclaration(t)) {
215         return;
216       } else if (ts.isIndexSignatureDeclaration(t)) {
217         // probably safe to ignore these
218         // [key: string]: boolean | number | string | undefined;
219         // and InitializeResult: [custom: string]: any;]
220         return
221       } else
222         throw new Error(`217 unexpected ${strKind(t)}`)
223     };
224     v.members.forEach(f);
225     if (mems.length == 0 && !v.heritageClauses &&
226       v.name.getText() != 'InitializedParams') {
227       return  // really? (Don't seem to need any of these)
228     };
229     // Found one we want
230     let x = newData(v, goName(v.name.getText()));
231     x.properties = ts.createNodeArray<ts.TypeElement>(mems);
232     if (v.typeParameters) x.generics = v.typeParameters;
233     if (v.heritageClauses) x.as = v.heritageClauses;
234     if (x.generics.length > 1) {  // Unneeded
235       // Item interface Item<K, V>...
236       return
237     };
238     if (data.has(x.name)) {  // modifying one we've seen
239       x = dataMerge(x, data.get(x.name));
240     }
241     data.set(x.name, x);
242   } else if (ts.isTypeAliasDeclaration(node)) {
243     const v: ts.TypeAliasDeclaration = node;
244     let x = newData(v, v.name.getText());
245     x.alias = v.type;
246     // if type is a union of constants, we (mostly) don't want it
247     // (at the top level)
248     // Unfortunately this is false for TraceValues
249     if (ts.isUnionTypeNode(v.type) &&
250       v.type.types.every((n: ts.TypeNode) => ts.isLiteralTypeNode(n))) {
251       if (x.name != 'TraceValues') return;
252     }
253     if (v.typeParameters) {
254       x.generics = v.typeParameters;
255     }
256     if (data.has(x.name)) x = dataMerge(x, data.get(x.name));
257     if (x.generics.length > 1) {
258       return
259     };
260     data.set(x.name, x);
261   } else if (ts.isModuleDeclaration(node)) {
262     const v: ts.ModuleDeclaration = node;
263     if (!ts.isModuleBlock(v.body)) {
264       throw new Error(`${loc(v)} not ModuleBlock, but ${strKind(v.body)}`)
265     }
266     const b: ts.ModuleBlock = v.body;
267     var s: ts.Statement[] = [];
268     // we don't want most of these
269     const fx = function (x: ts.Statement) {
270       if (ts.isFunctionDeclaration(x)) {
271         return
272       };
273       if (ts.isTypeAliasDeclaration(x) || ts.isModuleDeclaration(x)) {
274         return
275       };
276       if (!ts.isVariableStatement(x))
277         throw new Error(
278           `expected VariableStatment ${loc(x)} ${strKind(x)} ${x.getText()}`);
279       if (hasNewExpression(x)) {
280         return
281       };
282       s.push(x);
283     };
284     b.statements.forEach(fx)
285     if (s.length == 0) {
286       return
287     };
288     let m = newData(node, v.name.getText());
289     m.statements = ts.createNodeArray<ts.Statement>(s);
290     if (data.has(m.name)) m = dataMerge(m, data.get(m.name));
291     data.set(m.name, m);
292   } else if (ts.isEnumDeclaration(node)) {
293     const nm = node.name.getText();
294     let v = newData(node, nm);
295     v.enums = node.members;
296     if (data.has(nm)) {
297       v = dataMerge(v, data.get(nm));
298     }
299     data.set(nm, v);
300   } else if (ts.isClassDeclaration(node)) {
301     const v: ts.ClassDeclaration = node;
302     var d: ts.PropertyDeclaration[] = [];
303     // look harder at the PropertyDeclarations.
304     const wanted = function (c: ts.ClassElement): string {
305       if (ts.isConstructorDeclaration(c)) {
306         return ''
307       };
308       if (ts.isMethodDeclaration(c)) {
309         return ''
310       };
311       if (ts.isGetAccessor(c)) {
312         return ''
313       };
314       if (ts.isSetAccessor(c)) {
315         return ''
316       };
317       if (ts.isPropertyDeclaration(c)) {
318         d.push(c);
319         return strKind(c)
320       };
321       throw new Error(`Class decl ${strKind(c)} `)
322     };
323     v.members.forEach((c, i) => wanted(c));
324     if (d.length == 0) {
325       return
326     };  // don't need it, maybe
327     let c = newData(v, v.name.getText());
328     c.members = ts.createNodeArray<ts.PropertyDeclaration>(d);
329     if (v.typeParameters) {
330       c.generics = v.typeParameters
331     }
332     if (c.generics.length > 1) {
333       return
334     }
335     if (v.heritageClauses) {
336       c.as = v.heritageClauses
337     }
338     if (data.has(c.name))
339       throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`);
340     data.set(c.name, c);
341   } else {
342     throw new Error(`338 unexpected ${strKind(node)} ${loc(node)} `)
343   }
344 }
345
346 // Typescript can accumulate
347 function dataMerge(a: Data, b: Data): Data {
348   // maybe they are textually identical? (it happens)
349   const [at, bt] = [a.me.getText(), b.me.getText()];
350   if (at == bt) {
351     return a;
352   }
353   const ax = `(${a.statements.length},${a.properties.length})`
354   const bx = `(${b.statements.length},${b.properties.length})`
355   switch (a.name) {
356     case 'InitializeError':
357     case 'MessageType':
358     case 'CompletionItemTag':
359     case 'SymbolTag':
360     case 'CodeActionKind':
361       // want the Module
362       return a.statements.length > 0 ? a : b;
363     case 'CancellationToken':
364     case 'CancellationStrategy':
365       // want the Interface
366       return a.properties.length > 0 ? a : b;
367     case 'TextDocumentContentChangeEvent':  // almost the same
368     case 'TokenFormat':
369       return a;
370   }
371   console.log(
372     `367 ${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
373   throw new Error(`Fix dataMerge for ${a.name}`)
374 }
375
376 // is a node an ancestor of a NewExpression
377 function hasNewExpression(n: ts.Node): boolean {
378   let ans = false;
379   n.forEachChild((n: ts.Node) => {
380     if (ts.isNewExpression(n)) ans = true;
381   })
382   return ans
383 }
384
385 function checkOnce() {
386   // Data for all the rpc types?
387   rpcTypes.forEach(s => {
388     if (!data.has(s)) throw new Error(`checkOnce, ${s}?`)
389   });
390 }
391
392 // helper function to find underlying types
393 function underlying(n: ts.Node, f: (n: ts.Node) => void) {
394   if (!n) return;
395   const ff = function (n: ts.Node) {
396     underlying(n, f)
397   };
398   if (ts.isIdentifier(n)) {
399     f(n)
400   } else if (
401     n.kind == ts.SyntaxKind.StringKeyword ||
402     n.kind == ts.SyntaxKind.NumberKeyword ||
403     n.kind == ts.SyntaxKind.AnyKeyword ||
404     n.kind == ts.SyntaxKind.NullKeyword ||
405     n.kind == ts.SyntaxKind.BooleanKeyword ||
406     n.kind == ts.SyntaxKind.ObjectKeyword ||
407     n.kind == ts.SyntaxKind.VoidKeyword) {
408     // nothing to do
409   } else if (ts.isTypeReferenceNode(n)) {
410     f(n.typeName)
411   } else if (ts.isArrayTypeNode(n)) {
412     underlying(n.elementType, f)
413   } else if (ts.isHeritageClause(n)) {
414     n.types.forEach(ff);
415   } else if (ts.isExpressionWithTypeArguments(n)) {
416     underlying(n.expression, f)
417   } else if (ts.isPropertySignature(n)) {
418     underlying(n.type, f)
419   } else if (ts.isTypeLiteralNode(n)) {
420     n.members.forEach(ff)
421   } else if (ts.isUnionTypeNode(n) || ts.isIntersectionTypeNode(n)) {
422     n.types.forEach(ff)
423   } else if (ts.isIndexSignatureDeclaration(n)) {
424     underlying(n.type, f)
425   } else if (ts.isParenthesizedTypeNode(n)) {
426     underlying(n.type, f)
427   } else if (
428     ts.isLiteralTypeNode(n) || ts.isVariableStatement(n) ||
429     ts.isTupleTypeNode(n)) {
430     // we only see these in moreTypes, but they are handled elsewhere
431     return;
432   } else if (ts.isEnumMember(n)) {
433     if (ts.isStringLiteral(n.initializer)) return;
434     throw new Error(`EnumMember ${strKind(n.initializer)} ${n.name.getText()}`)
435   } else {
436     throw new Error(`saw ${strKind(n)} in underlying. ${n.getText()}`)
437   }
438 }
439
440 // find all the types implied by seenTypes.
441 // Simplest way to the transitive closure is to stabilize the size of seenTypes
442 // but it is slow
443 function moreTypes() {
444   const extra = function (s: string) {
445     if (!data.has(s)) throw new Error(`moreTypes needs ${s}`);
446     seenTypes.set(s, data.get(s))
447   };
448   rpcTypes.forEach(extra);  // all the types needed by the rpcs
449   // needed in enums.go (or elsewhere)
450   extra('InitializeError')
451   extra('WatchKind')
452   extra('FoldingRangeKind')
453   // not sure why these weren't picked up
454   extra('FileSystemWatcher')
455   extra('DidChangeWatchedFilesRegistrationOptions')
456   extra('WorkDoneProgressBegin')
457   extra('WorkDoneProgressReport')
458   extra('WorkDoneProgressEnd')
459   let old = 0
460   do {
461     old = seenTypes.size
462
463     const m = new Map<string, Data>();
464     const add = function (n: ts.Node) {
465       const nm = goName(n.getText());
466       if (seenTypes.has(nm) || m.has(nm)) return;
467       // For generic parameters, this might set it to undefined
468       m.set(nm, data.get(nm));
469     };
470     // expect all the heritage clauses have single Identifiers
471     const h = function (n: ts.Node) {
472       underlying(n, add);
473     };
474     const f = function (x: ts.NodeArray<ts.Node>) {
475       x.forEach(h)
476     };
477     seenTypes.forEach((d: Data) => d && f(d.as))
478     // find the types in the properties
479     seenTypes.forEach((d: Data) => d && f(d.properties))
480     // and in the alias and in the statements and in the enums
481     seenTypes.forEach((d: Data) => d && underlying(d.alias, add))
482     seenTypes.forEach((d: Data) => d && f(d.statements))
483     seenTypes.forEach((d: Data) => d && f(d.enums))
484     m.forEach((d, k) => seenTypes.set(k, d))
485   }
486   while (seenTypes.size != old)
487     ;
488 }
489
490 let typesOut = new Array<string>();
491 let constsOut = new Array<string>();
492
493 // generate Go types
494 function toGo(d: Data, nm: string) {
495   if (!d) return;  // this is probably a generic T
496   if (d.alias) {
497     goTypeAlias(d, nm);
498   } else if (d.statements.length > 0) {
499     goModule(d, nm);
500   } else if (d.enums.length > 0) {
501     goEnum(d, nm);
502   } else if (
503     d.properties.length > 0 || d.as.length > 0 || nm == 'InitializedParams') {
504     goInterface(d, nm);
505   } else
506     throw new Error(
507       `more cases in toGo ${nm} ${d.as.length} ${d.generics.length} `)
508 }
509
510 // these fields need a *
511 var starred: [string, string][] = [
512   ['TextDocumentContentChangeEvent', 'range'], ['CodeAction', 'command'],
513   ['DidSaveTextDocumentParams', 'text'], ['CompletionItem', 'command']
514 ];
515
516 // generate Go code for an interface
517 function goInterface(d: Data, nm: string) {
518   let ans = `type ${goName(nm)} struct {\n`;
519
520   // generate the code for each member
521   const g = function (n: ts.TypeElement) {
522     if (!ts.isPropertySignature(n))
523       throw new Error(`expected PropertySignature got ${strKind(n)} `);
524     ans = ans.concat(getComments(n));
525     const json = u.JSON(n);
526     // SelectionRange is a recursive type
527     let gt = goType(n.type, n.name.getText(), nm);
528     if (gt == d.name) gt = '*' + gt;  // avoid recursive types
529     // there are several cases where a * is needed
530     starred.forEach(([a, b]) => {
531       if (d.name == a && n.name.getText() == b) {
532         gt = '*' + gt;
533       };
534     })
535     ans = ans.concat(`${goName(n.name.getText())} ${gt}`, json, '\n')
536   };
537   d.properties.forEach(g)
538   // heritage clauses become embedded types
539   // check they are all Identifiers
540   const f = function (n: ts.ExpressionWithTypeArguments) {
541     if (!ts.isIdentifier(n.expression))
542       throw new Error(`Interface ${nm} heritage ${strKind(n.expression)} `);
543     ans = ans.concat(goName(n.expression.getText()), '\n')
544   };
545   d.as.forEach((n: ts.HeritageClause) => n.types.forEach(f))
546   ans = ans.concat(`}\n`);
547   typesOut.push(getComments(d.me))
548   typesOut.push(ans)
549 }
550
551 // generate Go code for a module (const declarations)
552 // Generates type definitions, and named constants
553 function goModule(d: Data, nm: string) {
554   if (d.generics.length > 0 || d.as.length > 0) {
555     throw new Error(`goModule: unexpected for ${nm}
556   `)
557   }
558   // all the statements should be export const <id>: value
559   //   or value = value
560   // They are VariableStatements with x.declarationList having a single
561   //   VariableDeclaration
562   let isNumeric = false;
563   const f = function (n: ts.Statement, i: number) {
564     if (!ts.isVariableStatement(n)) {
565       throw new Error(` ${nm} ${i} expected VariableStatement,
566       got ${strKind(n)}`);
567     }
568     const c = getComments(n)
569     const v = n.declarationList.declarations[0];  // only one
570
571     if (!v.initializer)
572       throw new Error(`no initializer ${nm} ${i} ${v.name.getText()}`);
573     isNumeric = strKind(v.initializer) == 'NumericLiteral';
574     if (c != '') constsOut.push(c);  // no point if there are no comments
575     // There are duplicates.
576     const cname = constName(goName(v.name.getText()), nm);
577     let val = v.initializer.getText()
578     val = val.split('\'').join('"')  // useless work for numbers
579     constsOut.push(`${cname} ${nm} = ${val}`)
580   };
581   d.statements.forEach(f)
582   typesOut.push(getComments(d.me))
583   // Or should they be type aliases?
584   typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`)
585 }
586
587 // generate Go code for an enum. Both types and named constants
588 function goEnum(d: Data, nm: string) {
589   let isNumeric = false
590   const f = function (v: ts.EnumMember, j: number) {  // same as goModule
591     if (!v.initializer)
592       throw new Error(`goEnum no initializer ${nm} ${j} ${v.name.getText()}`);
593     isNumeric = strKind(v.initializer) == 'NumericLiteral';
594     const c = getComments(v);
595     const cname = constName(goName(v.name.getText()), nm);
596     let val = v.initializer.getText()
597     val = val.split('\'').join('"')  // replace quotes. useless work for numbers
598     constsOut.push(`${c}${cname} ${nm} = ${val}`)
599   };
600   d.enums.forEach(f)
601   typesOut.push(getComments(d.me))
602   // Or should they be type aliases?
603   typesOut.push(`type ${nm} ${isNumeric ? 'float64' : 'string'}`)
604 }
605
606 // generate code for a type alias
607 function goTypeAlias(d: Data, nm: string) {
608   if (d.as.length != 0 || d.generics.length != 0) {
609     if (nm != 'ServerCapabilities')
610       throw new Error(`${nm} has extra fields(${d.as.length},${
611         d.generics.length}) ${d.me.getText()}`);
612   }
613   typesOut.push(getComments(d.me))
614   // d.alias doesn't seem to have comments
615   let aliasStr = goName(nm) == 'DocumentURI' ? ' ' : ' = '
616   typesOut.push(`type ${goName(nm)}${aliasStr}${goType(d.alias, nm)}\n`)
617 }
618
619 // return a go type and maybe an assocated javascript tag
620 function goType(n: ts.TypeNode, nm: string, parent?: string): string {
621   if (n.getText() == 'T') return 'interface{}';  // should check it's generic
622   if (ts.isTypeReferenceNode(n)) {
623     return goName(n.typeName.getText());  // avoid <T>
624   } else if (ts.isUnionTypeNode(n)) {
625     return goUnionType(n, nm, parent);
626   } else if (ts.isIntersectionTypeNode(n)) {
627     return goIntersectionType(n, nm);
628   } else if (strKind(n) == 'StringKeyword') {
629     return 'string';
630   } else if (strKind(n) == 'NumberKeyword') {
631     return 'float64';
632   } else if (strKind(n) == 'BooleanKeyword') {
633     return 'bool';
634   } else if (strKind(n) == 'AnyKeyword') {
635     return 'interface{}';
636   } else if (strKind(n) == 'NullKeyword') {
637     return 'nil'
638   } else if (strKind(n) == 'VoidKeyword' || strKind(n) == 'NeverKeyword') {
639     return 'void'
640   } else if (strKind(n) == 'ObjectKeyword') {
641     return 'interface{}'
642   } else if (ts.isArrayTypeNode(n)) {
643     if (nm === 'arguments') {
644       // Command and ExecuteCommandParams
645       return '[]json.RawMessage';
646     }
647     return `[]${goType(n.elementType, nm)}`
648   } else if (ts.isParenthesizedTypeNode(n)) {
649     return goType(n.type, nm)
650   } else if (ts.isLiteralTypeNode(n)) {
651     return strKind(n.literal) == 'StringLiteral' ? 'string' : 'float64';
652   } else if (ts.isTypeLiteralNode(n)) {
653     // these are anonymous structs
654     const v = goTypeLiteral(n, nm);
655     return v
656   } else if (ts.isTupleTypeNode(n)) {
657     if (n.getText() == '[number, number]') return '[]float64';
658     throw new Error(`goType unexpected Tuple ${n.getText()}`)
659   }
660   throw new Error(`${strKind(n)} goType unexpected ${n.getText()} for ${nm}`)
661 }
662
663 // The choice is uniform interface{}, or some heuristically assigned choice,
664 // or some better sytematic idea I haven't thought of. Using interface{}
665 // is, in practice, impossibly complex in the existing code.
666 function goUnionType(n: ts.UnionTypeNode, nm: string, parent?: string): string {
667   let help = `/*${n.getText()}*/`  // show the original as a comment
668   // There are some bad cases with newlines:
669   // range?: boolean | {\n      };
670   // full?: boolean | {\n               /**\n            * The server supports deltas for full documents.\n              */\n           delta?: boolean;\n      }
671   // These are handled specially:
672   if (parent == 'SemanticTokensOptions') {
673     if (nm == 'range') help = help.replace(/\n/, '');
674     if (nm == 'full') help = '/*boolean | <elided struct>*/';
675   }
676   // handle all the special cases
677   switch (n.types.length) {
678     case 2:
679       const a = strKind(n.types[0])
680       const b = strKind(n.types[1])
681       if (a == 'NumberKeyword' && b == 'StringKeyword') {  // ID
682         return `interface{} ${help}`
683       }
684       if (b == 'NullKeyword') {
685         if (nm == 'textDocument/codeAction') {
686           // (Command | CodeAction)[] | null
687           return `[]CodeAction ${help}`
688         }
689         let v = goType(n.types[0], 'a')
690         if (v.startsWith(`[]interface`)) v = v.slice(2, v.length)
691         return `${v} ${help}`
692       }
693       if (a == 'BooleanKeyword') {  // usually want bool
694         if (nm == 'codeActionProvider') return `interface{} ${help}`;
695         if (nm == 'renameProvider') return `interface{} ${help}`;
696         if (nm == 'save') return `${goType(n.types[1], '680')} ${help}`;
697         return `${goType(n.types[0], 'b')} ${help}`
698       }
699       if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}`;
700       if (help.includes('InsertReplaceEdit') && n.types[0].getText() == 'TextEdit') {
701         return `*TextEdit ${help}`
702       }
703       if (a == 'TypeReference' && a == b) return `interface{} ${help}`;
704       if (a == 'StringKeyword') return `string ${help}`;
705       if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
706         return `${goType(n.types[0], nm)}`
707       }
708       throw new Error(`691 ${a} ${b} ${n.getText()} ${loc(n)}`);
709     case 3:
710       const aa = strKind(n.types[0])
711       const bb = strKind(n.types[1])
712       const cc = strKind(n.types[2])
713       if (nm == 'textDocument/prepareRename') {
714         // want Range, not interface{}
715         return `${goType(n.types[0], nm)} ${help}`
716       }
717       if (nm == 'DocumentFilter') {
718         // not really a union. the first is enough, up to a missing
719         // omitempty but avoid repetitious comments
720         return `${goType(n.types[0], 'g')}`
721       }
722       if (nm == 'textDocument/documentSymbol') {
723         return `[]interface{} ${help}`
724       }
725       if (aa == 'TypeReference' && bb == 'ArrayType' && cc == 'NullKeyword') {
726         return `${goType(n.types[0], 'd')} ${help}`
727       }
728       if (aa == 'TypeReference' && bb == aa && cc == 'ArrayType') {
729         // should check that this is Hover.Contents
730         return `${goType(n.types[0], 'e')} ${help}`
731       }
732       if (aa == 'ArrayType' && bb == 'TypeReference' && cc == 'NullKeyword') {
733         // check this is nm == 'textDocument/completion'
734         return `${goType(n.types[1], 'f')} ${help}`
735       }
736       if (aa == 'LiteralType' && bb == aa && cc == aa) return `string ${help}`;
737       break;
738     case 4:
739       if (nm == 'documentChanges') return `TextDocumentEdit ${help} `;
740       if (nm == 'textDocument/prepareRename') return `Range ${help} `;
741     default:
742       throw new Error(`goUnionType len=${n.types.length} nm=${nm}`)
743   }
744
745   // Result will be interface{} with a comment
746   let isLiteral = true;
747   let literal = 'string';
748   let res = `interface{} /* `
749   n.types.forEach((v: ts.TypeNode, i: number) => {
750     // might get an interface inside:
751     //  (Command | CodeAction)[] | null
752     let m = goType(v, nm);
753     if (m.indexOf('interface') != -1) {
754       // avoid nested comments
755       m = m.split(' ')[0];
756     }
757     m = m.split('\n').join('; ')  // sloppy: struct{;
758     res = res.concat(`${i == 0 ? '' : ' | '}`, m)
759     if (!ts.isLiteralTypeNode(v)) isLiteral = false;
760     else literal = strKind(v.literal) == 'StringLiteral' ? 'string' : 'number';
761   });
762   if (!isLiteral) {
763     return res + '*/';
764   }
765   // trace?: 'off' | 'messages' | 'verbose' should get string
766   return `${literal} /* ${n.getText()} */`
767 }
768
769 // some of the intersection types A&B are ok as struct{A;B;} and some
770 // could be expanded, and ClientCapabilites has to be expanded,
771 // at least for workspace. It's possible to check algorithmically,
772 // but much simpler just to check explicity.
773 function goIntersectionType(n: ts.IntersectionTypeNode, nm: string): string {
774   if (nm == 'ClientCapabilities') return expandIntersection(n);
775   if (nm == 'ServerCapabilities') return expandIntersection(n);
776   let inner = '';
777   n.types.forEach(
778     (t: ts.TypeNode) => { inner = inner.concat(goType(t, nm), '\n') });
779   return `struct{ \n${inner}} `
780 }
781
782 // for each of the itersected types, extract its components (each will
783 // have a Data with properties) extract the properties, and keep track
784 // of them by name. The names that occur once can be output. The names
785 // that occur more than once need to be combined.
786 function expandIntersection(n: ts.IntersectionTypeNode): string {
787   const bad = function (n: ts.Node, s: string) {
788     return new Error(`expandIntersection ${strKind(n)} ${s}`)
789   };
790   let props = new Map<string, ts.PropertySignature[]>();
791   for (const tp of n.types) {
792     if (!ts.isTypeReferenceNode(tp)) throw bad(tp, 'A');
793     const d = data.get(goName(tp.typeName.getText()));
794     for (const p of d.properties) {
795       if (!ts.isPropertySignature(p)) throw bad(p, 'B');
796       let v = props.get(p.name.getText()) || [];
797       v.push(p);
798       props.set(p.name.getText(), v);
799     }
800   }
801   let ans = 'struct {\n';
802   for (const [k, v] of Array.from(props)) {
803     if (v.length == 1) {
804       const a = v[0];
805       ans = ans.concat(getComments(a));
806       ans = ans.concat(`${goName(k)} ${goType(a.type, k)} ${u.JSON(a)}\n`)
807       continue
808     }
809     ans = ans.concat(`${goName(k)} struct {\n`)
810     for (let i = 0; i < v.length; i++) {
811       const a = v[i];
812       if (ts.isTypeReferenceNode(a.type)) {
813         ans = ans.concat(getComments(a))
814         ans = ans.concat(goName(a.type.typeName.getText()), '\n');
815       } else if (ts.isTypeLiteralNode(a.type)) {
816         if (a.type.members.length != 1) throw bad(a.type, 'C');
817         const b = a.type.members[0];
818         if (!ts.isPropertySignature(b)) throw bad(b, 'D');
819         ans = ans.concat(getComments(b));
820         ans = ans.concat(
821           goName(b.name.getText()), ' ', goType(b.type, 'a'), u.JSON(b), '\n')
822       } else if (a.type.kind == ts.SyntaxKind.ObjectKeyword) {
823         ans = ans.concat(getComments(a))
824         ans = ans.concat(
825           goName(a.name.getText()), ' ', 'interface{}', u.JSON(a), '\n')
826       } else {
827         throw bad(a.type, `E ${a.getText()} in ${goName(k)} at ${loc(a)}`)
828       }
829     }
830     ans = ans.concat('}\n');
831   }
832   ans = ans.concat('}\n');
833   return ans
834 }
835
836 function goTypeLiteral(n: ts.TypeLiteralNode, nm: string): string {
837   let ans: string[] = [];  // in case we generate a new extra type
838   let res = 'struct{\n'    // the actual answer usually
839   const g = function (nx: ts.TypeElement) {
840     // add the json, as in goInterface(). Strange inside union types.
841     if (ts.isPropertySignature(nx)) {
842       let json = u.JSON(nx);
843       let typ = goType(nx.type, nx.name.getText())
844       const v = getComments(nx) || '';
845       starred.forEach(([a, b]) => {
846         if (a != nm || b != typ.toLowerCase()) return;
847         typ = '*' + typ;
848         json = json.substring(0, json.length - 2) + ',omitempty"`'
849       })
850       res = res.concat(`${v} ${goName(nx.name.getText())} ${typ}`, json, '\n')
851       ans.push(`${v}${goName(nx.name.getText())} ${typ} ${json}\n`)
852     } else if (ts.isIndexSignatureDeclaration(nx)) {
853       if (nx.getText() == '[uri: string]: TextEdit[];') {
854         res = 'map[string][]TextEdit';
855         ans.push(`map[string][]TextEdit`);  // this is never used
856         return
857       }
858       throw new Error(` handle ${nx.getText()}`)
859     } else
860       throw new Error(`TypeLiteral had ${strKind(nx)}`)
861   };
862   n.members.forEach(g)
863   // for some the generated type is wanted, for others it's not needed
864   if (!nm.startsWith('workspace')) {
865     if (res.startsWith('struct')) return res + '}';  // map[] is special
866     return res
867   }
868   extraTypes.set(goName(nm) + 'Gn', ans)
869   return goName(nm) + 'Gn'
870 }
871
872 // print all the types and constants and extra types
873 function outputTypes() {
874   // generate go types alphabeticaly
875   let v = Array.from(seenTypes.keys());
876   v.sort();
877   v.forEach((x) => toGo(seenTypes.get(x), x))
878   u.prgo(u.computeHeader(true))
879   u.prgo(`import "encoding/json"\n\n`);
880   typesOut.forEach((s) => {
881     u.prgo(s);
882     // it's more convenient not to have to think about trailing newlines
883     // when generating types, but doc comments can't have an extra \n
884     if (s.indexOf('/**') < 0) u.prgo('\n');
885   })
886   u.prgo('\nconst (\n');
887   constsOut.forEach((s) => {
888     u.prgo(s);
889     u.prgo('\n')
890   })
891   u.prgo(')\n');
892   u.prgo('// Types created to name formal parameters and embedded structs\n')
893   extraTypes.forEach((v, k) => {
894     u.prgo(` type ${k} struct {\n`)
895     v.forEach((s) => {
896       u.prgo(s);
897       u.prgo('\n')
898     });
899     u.prgo('}\n')
900   });
901 }
902
903 // client and server ------------------
904
905 interface side {
906   methods: string[];
907   cases: string[];
908   calls: string[];
909   name: string;    // client or server
910   goName: string;  // Client or Server
911   outputFile?: string;
912   fd?: number
913 }
914 let client: side = {
915   methods: [],
916   cases: [],
917   calls: [],
918   name: 'client',
919   goName: 'Client',
920 };
921 let server: side = {
922   methods: [],
923   cases: [],
924   calls: [],
925   name: 'server',
926   goName: 'Server',
927 };
928
929 // commonly used output
930 const notNil = `if len(r.Params()) > 0 {
931   return true, reply(ctx, nil, errors.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
932 }`;
933
934 // Go code for notifications. Side is client or server, m is the request
935 // method
936 function goNot(side: side, m: string) {
937   if (m == '$/cancelRequest') return;  // handled specially in protocol.go
938   const n = not.get(m);
939   const a = goType(n.typeArguments[0], m);
940   const nm = methodName(m);
941   side.methods.push(sig(nm, a, ''));
942   const caseHdr = ` case "${m}":  // notif`;
943   let case1 = notNil;
944   if (a != '' && a != 'void') {
945     case1 = `var params ${a}
946     if err := json.Unmarshal(r.Params(), &params); err != nil {
947       return true, sendParseError(ctx, reply, err)
948     }
949     err:= ${side.name}.${nm}(ctx, &params)
950     return true, reply(ctx, nil, err)`
951   } else {
952     case1 = `err := ${side.name}.${nm}(ctx)
953     return true, reply(ctx, nil, err)`;
954   }
955   side.cases.push(`${caseHdr}\n${case1}`);
956
957   const arg3 = a == '' || a == 'void' ? 'nil' : 'params';
958   side.calls.push(`
959   func (s *${side.name}Dispatcher) ${sig(nm, a, '', true)} {
960     return s.Conn.Notify(ctx, "${m}", ${arg3})
961   }`);
962 }
963
964 // Go code for requests.
965 function goReq(side: side, m: string) {
966   const n = req.get(m);
967   const nm = methodName(m);
968   let a = goType(n.typeArguments[0], m);
969   let b = goType(n.typeArguments[1], m);
970   if (n.getText().includes('Type0')) {
971     b = a;
972     a = '';  // workspace/workspaceFolders and shutdown
973   }
974   u.prb(`${side.name} req ${a != ''}, ${b != ''} ${nm} ${m} ${loc(n)} `)
975   side.methods.push(sig(nm, a, b));
976
977   const caseHdr = `case "${m}": // req`;
978   let case1 = notNil;
979   if (a != '') {
980     if (extraTypes.has('Param' + nm)) a = 'Param' + nm
981     case1 = `var params ${a}
982     if err := json.Unmarshal(r.Params(), &params); err != nil {
983       return true, sendParseError(ctx, reply, err)
984     }`;
985   }
986   const arg2 = a == '' ? '' : ', &params';
987   let case2 = `if err := ${side.name}.${nm}(ctx${arg2}); err != nil {
988     event.Error(ctx, "", err)
989   }`;
990   if (b != '' && b != 'void') {
991     case2 = `resp, err := ${side.name}.${nm}(ctx${arg2})
992     return true, reply(ctx, resp, err)`;
993   } else {  // response is nil
994     case2 = `err := ${side.name}.${nm}(ctx${arg2})
995     return true, reply(ctx, nil, err)`
996   }
997
998   side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
999
1000   const callHdr = `func (s *${side.name}Dispatcher) ${sig(nm, a, b, true)} {`;
1001   let callBody = `return Call(ctx, s.Conn, "${m}", nil, nil)\n}`;
1002   if (b != '' && b != 'void') {
1003     const p2 = a == '' ? 'nil' : 'params';
1004     const returnType = indirect(b) ? `*${b}` : b;
1005     callBody = `var result ${returnType}
1006                         if err := Call(ctx, s.Conn, "${m}", ${
1007       p2}, &result); err != nil {
1008                                 return nil, err
1009       }
1010       return result, nil
1011     }`;
1012   } else if (a != '') {
1013     callBody = `return Call(ctx, s.Conn, "${m}", params, nil) // Call, not Notify
1014   }`
1015   }
1016   side.calls.push(`${callHdr}\n${callBody}\n`);
1017 }
1018
1019 // make sure method names are unique
1020 let seenNames = new Set<string>();
1021 function methodName(m: string): string {
1022   let i = m.indexOf('/');
1023   let s = m.substring(i + 1);
1024   let x = s[0].toUpperCase() + s.substring(1);
1025   for (let j = x.indexOf('/'); j >= 0; j = x.indexOf('/')) {
1026     let suffix = x.substring(j + 1)
1027     suffix = suffix[0].toUpperCase() + suffix.substring(1)
1028     let prefix = x.substring(0, j)
1029     x = prefix + suffix
1030   }
1031   if (seenNames.has(x)) {
1032     // Resolve, ResolveCodeLens, ResolveDocumentLink
1033     if (!x.startsWith('Resolve')) throw new Error(`expected Resolve, not ${x}`)
1034     x += m[0].toUpperCase() + m.substring(1, i)
1035   }
1036   seenNames.add(x);
1037   return x;
1038 }
1039
1040 // used in sig and in goReq
1041 function indirect(s: string): boolean {
1042   if (s == '' || s == 'void') return false;
1043   const skip = (x: string) => s.startsWith(x);
1044   if (skip('[]') || skip('interface') || skip('Declaration') ||
1045     skip('Definition') || skip('DocumentSelector'))
1046     return false;
1047   return true
1048 }
1049
1050 // Go signatures for methods.
1051 function sig(nm: string, a: string, b: string, names?: boolean): string {
1052   if (a.indexOf('struct') != -1) {
1053     const v = a.split('\n')
1054     extraTypes.set(`Param${nm}`, v.slice(1, v.length - 1))
1055     a = 'Param' + nm
1056   }
1057   if (a == 'void')
1058     a = '';
1059   else if (a != '') {
1060     if (names)
1061       a = ', params *' + a;
1062     else
1063       a = ', *' + a;
1064   }
1065   let ret = 'error';
1066   if (b != '' && b != 'void') {
1067     // avoid * when it is senseless
1068     if (indirect(b)) b = '*' + b;
1069     ret = `(${b}, error)`;
1070   }
1071   let start = `${nm}(`;
1072   if (names) {
1073     start = start + 'ctx ';
1074   }
1075   return `${start}context.Context${a}) ${ret}`;
1076 }
1077
1078 // write the request/notification code
1079 function output(side: side) {
1080   // make sure the output file exists
1081   if (!side.outputFile) {
1082     side.outputFile = `ts${side.name}.go`;
1083     side.fd = fs.openSync(side.outputFile, 'w');
1084   }
1085   const f = function (s: string) {
1086     fs.writeSync(side.fd, s);
1087     fs.writeSync(side.fd, '\n');
1088   };
1089   f(u.computeHeader(false));
1090   f(`
1091         import (
1092           "context"
1093           "encoding/json"
1094
1095           "golang.org/x/tools/internal/jsonrpc2"
1096           errors "golang.org/x/xerrors"
1097         )
1098         `);
1099   const a = side.name[0].toUpperCase() + side.name.substring(1)
1100   f(`type ${a} interface {`);
1101   side.methods.forEach((v) => { f(v) });
1102   f('}\n');
1103   f(`func ${side.name}Dispatch(ctx context.Context, ${side.name} ${a}, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) {
1104           switch r.Method() {`);
1105   side.cases.forEach((v) => { f(v) });
1106   f(`
1107         default:
1108           return false, nil
1109         }
1110       }`);
1111   side.calls.forEach((v) => { f(v) });
1112 }
1113
1114 // Handling of non-standard requests, so we can add gopls-specific calls.
1115 function nonstandardRequests() {
1116   server.methods.push(
1117     'NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)')
1118   server.calls.push(
1119     `func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
1120       var result interface{}
1121       if err := Call(ctx, s.Conn, method, params, &result); err != nil {
1122         return nil, err
1123       }
1124       return result, nil
1125     }
1126   `)
1127 }
1128
1129 // ----- remember it's a scripting language
1130 function main() {
1131   if (u.gitHash != u.git()) {
1132     throw new Error(
1133       `git hash mismatch, wanted\n${u.gitHash} but source is at\n${u.git()}`);
1134   }
1135   u.createOutputFiles()
1136   parse()
1137   u.printAST(program)
1138   // find the Requests and Nofificatations
1139   for (const sourceFile of program.getSourceFiles()) {
1140     if (!sourceFile.isDeclarationFile) {
1141       ts.forEachChild(sourceFile, findRPCs)
1142     }
1143   }
1144   // separate RPCs into client and server
1145   setReceives();
1146   // visit every sourceFile collecting top-level type definitions
1147   for (const sourceFile of program.getSourceFiles()) {
1148     if (!sourceFile.isDeclarationFile) {
1149       ts.forEachChild(sourceFile, genTypes)
1150     }
1151   }
1152   // check that each thing occurs exactly once, and put pointers into
1153   // seenTypes
1154   checkOnce();
1155   // for each of Client and Server there are 3 parts to the output:
1156   // 1. type X interface {methods}
1157   // 2. func (h *serverHandler) Deliver(...) { switch r.method }
1158   // 3. func (x *xDispatcher) Method(ctx, parm)
1159   not.forEach(  // notifications
1160     (v, k) => {
1161       receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k)
1162     });
1163   req.forEach(  // requests
1164     (v, k) => {
1165       receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k)
1166     });
1167   nonstandardRequests();
1168   // find all the types implied by seenTypes and rpcs to try to avoid
1169   // generating types that aren't used
1170   moreTypes();
1171   // and print the Go code
1172   outputTypes()
1173   console.log(`seen ${seenTypes.size + extraTypes.size}`)
1174   output(client);
1175   output(server);
1176 }
1177
1178 main()