Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / 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:${f(d.statements)} e:${f(d.enums)} m:${f(d.members)} ${d.alias != undefined}`
189 }
190
191 let data = new Map<string, Data>();            // parsed data types
192 let seenTypes = new Map<string, Data>();       // type names we've seen
193 let extraTypes = new Map<string, string[]>();  // to avoid struct params
194
195 // look at top level data definitions
196 function genTypes(node: ts.Node) {
197   // Ignore top-level items that can't produce output
198   if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
199     ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
200     ts.isExportDeclaration(node) || ts.isEmptyStatement(node) ||
201     ts.isExportAssignment(node) || ts.isImportEqualsDeclaration(node) ||
202     ts.isBlock(node) || node.kind == ts.SyntaxKind.EndOfFileToken) {
203     return;
204   }
205   if (ts.isInterfaceDeclaration(node)) {
206     const v: ts.InterfaceDeclaration = node;
207     // need to check the members, many of which are disruptive
208     let mems: ts.TypeElement[] = [];
209     const f = function (t: ts.TypeElement) {
210       if (ts.isPropertySignature(t)) {
211         mems.push(t);
212       } else if (ts.isMethodSignature(t) || ts.isCallSignatureDeclaration(t)) {
213         return;
214       } else if (ts.isIndexSignatureDeclaration(t)) {
215         // probably safe to ignore these
216         // [key: string]: boolean | number | string | undefined;
217         // and InitializeResult: [custom: string]: any;]
218         return
219       } else
220         throw new Error(`217 unexpected ${strKind(t)}`)
221     };
222     v.members.forEach(f);
223     if (mems.length == 0 && !v.heritageClauses &&
224       v.name.getText() != 'InitializedParams') {
225       return  // really? (Don't seem to need any of these)
226     };
227     // Found one we want
228     let x = newData(v, goName(v.name.getText()));
229     x.properties = ts.createNodeArray<ts.TypeElement>(mems);
230     if (v.typeParameters) x.generics = v.typeParameters;
231     if (v.heritageClauses) x.as = v.heritageClauses;
232     if (x.generics.length > 1) {  // Unneeded
233       // Item interface Item<K, V>...
234       return
235     };
236     if (data.has(x.name)) {  // modifying one we've seen
237       x = dataMerge(x, data.get(x.name));
238     }
239     data.set(x.name, x);
240   } else if (ts.isTypeAliasDeclaration(node)) {
241     const v: ts.TypeAliasDeclaration = node;
242     let x = newData(v, v.name.getText());
243     x.alias = v.type;
244     // if type is a union of constants, we (mostly) don't want it
245     // (at the top level)
246     // Unfortunately this is false for TraceValues
247     if (ts.isUnionTypeNode(v.type) &&
248       v.type.types.every((n: ts.TypeNode) => ts.isLiteralTypeNode(n))) {
249       if (x.name != 'TraceValues') return;
250     }
251     if (v.typeParameters) {
252       x.generics = v.typeParameters;
253     }
254     if (data.has(x.name)) x = dataMerge(x, data.get(x.name));
255     if (x.generics.length > 1) {
256       return
257     };
258     data.set(x.name, x);
259   } else if (ts.isModuleDeclaration(node)) {
260     const v: ts.ModuleDeclaration = node;
261     if (!ts.isModuleBlock(v.body)) {
262       throw new Error(`${loc(v)} not ModuleBlock, but ${strKind(v.body)}`)
263     }
264     const b: ts.ModuleBlock = v.body;
265     var s: ts.Statement[] = [];
266     // we don't want most of these
267     const fx = function (x: ts.Statement) {
268       if (ts.isFunctionDeclaration(x)) {
269         return
270       };
271       if (ts.isTypeAliasDeclaration(x) || ts.isModuleDeclaration(x)) {
272         return
273       };
274       if (!ts.isVariableStatement(x))
275         throw new Error(
276           `expected VariableStatment ${loc(x)} ${strKind(x)} ${x.getText()}`);
277       if (hasNewExpression(x)) {
278         return
279       };
280       s.push(x);
281     };
282     b.statements.forEach(fx)
283     if (s.length == 0) {
284       return
285     };
286     let m = newData(node, v.name.getText());
287     m.statements = ts.createNodeArray<ts.Statement>(s);
288     if (data.has(m.name)) m = dataMerge(m, data.get(m.name));
289     data.set(m.name, m);
290   } else if (ts.isEnumDeclaration(node)) {
291     const nm = node.name.getText();
292     let v = newData(node, nm);
293     v.enums = node.members;
294     if (data.has(nm)) {
295       v = dataMerge(v, data.get(nm));
296     }
297     data.set(nm, v);
298   } else if (ts.isClassDeclaration(node)) {
299     const v: ts.ClassDeclaration = node;
300     var d: ts.PropertyDeclaration[] = [];
301     // look harder at the PropertyDeclarations.
302     const wanted = function (c: ts.ClassElement): string {
303       if (ts.isConstructorDeclaration(c)) {
304         return ''
305       };
306       if (ts.isMethodDeclaration(c)) {
307         return ''
308       };
309       if (ts.isGetAccessor(c)) {
310         return ''
311       };
312       if (ts.isSetAccessor(c)) {
313         return ''
314       };
315       if (ts.isPropertyDeclaration(c)) {
316         d.push(c);
317         return strKind(c)
318       };
319       throw new Error(`Class decl ${strKind(c)} `)
320     };
321     v.members.forEach((c, i) => wanted(c));
322     if (d.length == 0) {
323       return
324     };  // don't need it, maybe
325     let c = newData(v, v.name.getText());
326     c.members = ts.createNodeArray<ts.PropertyDeclaration>(d);
327     if (v.typeParameters) {
328       c.generics = v.typeParameters
329     }
330     if (c.generics.length > 1) {
331       return
332     }
333     if (v.heritageClauses) {
334       c.as = v.heritageClauses
335     }
336     if (data.has(c.name))
337       throw new Error(`Class dup ${loc(c.me)} and ${loc(data.get(c.name).me)}`);
338     data.set(c.name, c);
339   } else {
340     throw new Error(`338 unexpected ${strKind(node)} ${loc(node)} `)
341   }
342 }
343
344 // Typescript can accumulate
345 function dataMerge(a: Data, b: Data): Data {
346   // maybe they are textually identical? (it happens)
347   const [at, bt] = [a.me.getText(), b.me.getText()];
348   if (at == bt) {
349     return a;
350   }
351   const ax = `(${a.statements.length},${a.properties.length})`
352   const bx = `(${b.statements.length},${b.properties.length})`
353   switch (a.name) {
354     case 'InitializeError':
355     case 'MessageType':
356     case 'CompletionItemTag':
357     case 'SymbolTag':
358     case 'CodeActionKind':
359       // want the Module
360       return a.statements.length > 0 ? a : b;
361     case 'CancellationToken':
362     case 'CancellationStrategy':
363       // want the Interface
364       return a.properties.length > 0 ? a : b;
365     case 'TextDocumentContentChangeEvent':  // almost the same
366     case 'TokenFormat':
367       return a;
368   }
369   console.log(
370     `367 ${strKind(a.me)} ${strKind(b.me)} ${a.name} ${loc(a.me)} ${loc(b.me)}`)
371   throw new Error(`Fix dataMerge for ${a.name}`)
372 }
373
374 // is a node an ancestor of a NewExpression
375 function hasNewExpression(n: ts.Node): boolean {
376   let ans = false;
377   n.forEachChild((n: ts.Node) => {
378     if (ts.isNewExpression(n)) ans = true;
379   })
380   return ans
381 }
382
383 function checkOnce() {
384   // Data for all the rpc types?
385   rpcTypes.forEach(s => {
386     if (!data.has(s)) throw new Error(`checkOnce, ${s}?`)
387   });
388 }
389
390 // helper function to find underlying types
391 function underlying(n: ts.Node, f: (n: ts.Node) => void) {
392   if (!n) return;
393   const ff = function (n: ts.Node) {
394     underlying(n, f)
395   };
396   if (ts.isIdentifier(n)) {
397     f(n)
398   } else if (
399     n.kind == ts.SyntaxKind.StringKeyword ||
400     n.kind == ts.SyntaxKind.NumberKeyword ||
401     n.kind == ts.SyntaxKind.AnyKeyword ||
402     n.kind == ts.SyntaxKind.UnknownKeyword ||
403     n.kind == ts.SyntaxKind.NullKeyword ||
404     n.kind == ts.SyntaxKind.BooleanKeyword ||
405     n.kind == ts.SyntaxKind.ObjectKeyword ||
406     n.kind == ts.SyntaxKind.VoidKeyword) {
407     // nothing to do
408   } else if (ts.isTypeReferenceNode(n)) {
409     f(n.typeName)
410   } else if (ts.isArrayTypeNode(n)) {
411     underlying(n.elementType, f)
412   } else if (ts.isHeritageClause(n)) {
413     n.types.forEach(ff);
414   } else if (ts.isExpressionWithTypeArguments(n)) {
415     underlying(n.expression, f)
416   } else if (ts.isPropertySignature(n)) {
417     underlying(n.type, f)
418   } else if (ts.isTypeLiteralNode(n)) {
419     n.members.forEach(ff)
420   } else if (ts.isUnionTypeNode(n) || ts.isIntersectionTypeNode(n)) {
421     n.types.forEach(ff)
422   } else if (ts.isIndexSignatureDeclaration(n)) {
423     underlying(n.type, f)
424   } else if (ts.isParenthesizedTypeNode(n)) {
425     underlying(n.type, f)
426   } else if (
427     ts.isLiteralTypeNode(n) || ts.isVariableStatement(n) ||
428     ts.isTupleTypeNode(n)) {
429     // we only see these in moreTypes, but they are handled elsewhere
430     return;
431   } else if (ts.isEnumMember(n)) {
432     if (ts.isStringLiteral(n.initializer)) return;
433     throw new Error(`EnumMember ${strKind(n.initializer)} ${n.name.getText()}`)
434   } else {
435     throw new Error(`saw ${strKind(n)} in underlying. ${n.getText()} at ${loc(n)}`)
436   }
437 }
438
439 // find all the types implied by seenTypes.
440 // Simplest way to the transitive closure is to stabilize the size of seenTypes
441 // but it is slow
442 function moreTypes() {
443   const extra = function (s: string) {
444     if (!data.has(s)) throw new Error(`moreTypes needs ${s}`);
445     seenTypes.set(s, data.get(s))
446   };
447   rpcTypes.forEach(extra);  // all the types needed by the rpcs
448   // needed in enums.go (or elsewhere)
449   extra('InitializeError')
450   extra('WatchKind')
451   extra('FoldingRangeKind')
452   // not sure why these weren't picked up
453   extra('FileSystemWatcher')
454   extra('DidChangeWatchedFilesRegistrationOptions')
455   extra('WorkDoneProgressBegin')
456   extra('WorkDoneProgressReport')
457   extra('WorkDoneProgressEnd')
458   let old = 0
459   do {
460     old = seenTypes.size
461
462     const m = new Map<string, Data>();
463     const add = function (n: ts.Node) {
464       const nm = goName(n.getText());
465       if (seenTypes.has(nm) || m.has(nm)) return;
466       // For generic parameters, this might set it to undefined
467       m.set(nm, data.get(nm));
468     };
469     // expect all the heritage clauses have single Identifiers
470     const h = function (n: ts.Node) {
471       underlying(n, add);
472     };
473     const f = function (x: ts.NodeArray<ts.Node>) {
474       x.forEach(h)
475     };
476     seenTypes.forEach((d: Data) => d && f(d.as))
477     // find the types in the properties
478     seenTypes.forEach((d: Data) => d && f(d.properties))
479     // and in the alias and in the statements and in the enums
480     seenTypes.forEach((d: Data) => d && underlying(d.alias, add))
481     seenTypes.forEach((d: Data) => d && f(d.statements))
482     seenTypes.forEach((d: Data) => d && f(d.enums))
483     m.forEach((d, k) => seenTypes.set(k, d))
484   }
485   while (seenTypes.size != old)
486     ;
487 }
488
489 let typesOut = new Array<string>();
490 let constsOut = new Array<string>();
491
492 // generate Go types
493 function toGo(d: Data, nm: string) {
494   if (!d) return;  // this is probably a generic T
495   if (d.alias) {
496     goTypeAlias(d, nm);
497   } else if (d.statements.length > 0) {
498     goModule(d, nm);
499   } else if (d.enums.length > 0) {
500     goEnum(d, nm);
501   } else if (
502     d.properties.length > 0 || d.as.length > 0 || nm == 'InitializedParams') {
503     goInterface(d, nm);
504   } else
505     throw new Error(
506       `more cases in toGo ${nm} ${d.as.length} ${d.generics.length} `)
507 }
508
509 // these fields need a *
510 var starred: [string, string][] = [
511   ['TextDocumentContentChangeEvent', 'range'], ['CodeAction', 'command'],
512   ['DidSaveTextDocumentParams', 'text'], ['CompletionItem', 'command'],
513   ['Diagnostic', 'codeDescription']
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},${d.generics.length}) ${d.me.getText()}`);
611   }
612   typesOut.push(getComments(d.me))
613   // d.alias doesn't seem to have comments
614   let aliasStr = goName(nm) == 'DocumentURI' ? ' ' : ' = '
615   typesOut.push(`type ${goName(nm)}${aliasStr}${goType(d.alias, nm)}\n`)
616 }
617
618 // return a go type and maybe an assocated javascript tag
619 function goType(n: ts.TypeNode, nm: string, parent?: string): string {
620   if (n.getText() == 'T') return 'interface{}';  // should check it's generic
621   if (ts.isTypeReferenceNode(n)) {
622     return goName(n.typeName.getText());  // avoid <T>
623   } else if (ts.isUnionTypeNode(n)) {
624     return goUnionType(n, nm, parent);
625   } else if (ts.isIntersectionTypeNode(n)) {
626     return goIntersectionType(n, nm);
627   } else if (strKind(n) == 'StringKeyword') {
628     return 'string';
629   } else if (strKind(n) == 'NumberKeyword') {
630     return 'float64';
631   } else if (strKind(n) == 'BooleanKeyword') {
632     return 'bool';
633   } else if (strKind(n) == 'AnyKeyword' || strKind(n) == 'UnknownKeyword') {
634     return 'interface{}';
635   } else if (strKind(n) == 'NullKeyword') {
636     return 'nil'
637   } else if (strKind(n) == 'VoidKeyword' || strKind(n) == 'NeverKeyword') {
638     return 'void'
639   } else if (strKind(n) == 'ObjectKeyword') {
640     return 'interface{}'
641   } else if (ts.isArrayTypeNode(n)) {
642     if (nm === 'arguments') {
643       // Command and ExecuteCommandParams
644       return '[]json.RawMessage';
645     }
646     return `[]${goType(n.elementType, nm)}`
647   } else if (ts.isParenthesizedTypeNode(n)) {
648     return goType(n.type, nm)
649   } else if (ts.isLiteralTypeNode(n)) {
650     return strKind(n.literal) == 'StringLiteral' ? 'string' : 'float64';
651   } else if (ts.isTypeLiteralNode(n)) {
652     // these are anonymous structs
653     const v = goTypeLiteral(n, nm);
654     return v
655   } else if (ts.isTupleTypeNode(n)) {
656     if (n.getText() == '[number, number]') return '[]float64';
657     throw new Error(`goType unexpected Tuple ${n.getText()}`)
658   }
659   throw new Error(`${strKind(n)} goType unexpected ${n.getText()} for ${nm}`)
660 }
661
662 // The choice is uniform interface{}, or some heuristically assigned choice,
663 // or some better sytematic idea I haven't thought of. Using interface{}
664 // is, in practice, impossibly complex in the existing code.
665 function goUnionType(n: ts.UnionTypeNode, nm: string, parent?: string): string {
666   let help = `/*${n.getText()}*/`  // show the original as a comment
667   // There are some bad cases with newlines:
668   // range?: boolean | {\n      };
669   // full?: boolean | {\n               /**\n            * The server supports deltas for full documents.\n              */\n           delta?: boolean;\n      }
670   // These are handled specially:
671   if (nm == 'range') help = help.replace(/\n/, '');
672   if (nm == 'full' && help.indexOf('\n') != -1) {
673     help = '/*boolean | <elided struct>*/';
674   }
675   // handle all the special cases
676   switch (n.types.length) {
677     case 2:
678       const a = strKind(n.types[0])
679       const b = strKind(n.types[1])
680       if (a == 'NumberKeyword' && b == 'StringKeyword') {  // ID
681         return `interface{} ${help}`
682       }
683       if (b == 'NullKeyword') {
684         if (nm == 'textDocument/codeAction') {
685           // (Command | CodeAction)[] | null
686           return `[]CodeAction ${help}`
687         }
688         let v = goType(n.types[0], 'a')
689         if (v.startsWith(`[]interface`)) v = v.slice(2, v.length)
690         return `${v} ${help}`
691       }
692       if (a == 'BooleanKeyword') {  // usually want bool
693         if (nm == 'codeActionProvider') return `interface{} ${help}`;
694         if (nm == 'renameProvider') return `interface{} ${help}`;
695         if (nm == 'full') return `interface{} ${help}`; // there's a struct
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}", ${p2}, &result); err != nil {
1007                                 return nil, err
1008       }
1009       return result, nil
1010     }`;
1011   } else if (a != '') {
1012     callBody = `return Call(ctx, s.Conn, "${m}", params, nil) // Call, not Notify
1013   }`
1014   }
1015   side.calls.push(`${callHdr}\n${callBody}\n`);
1016 }
1017
1018 // make sure method names are unique
1019 let seenNames = new Set<string>();
1020 function methodName(m: string): string {
1021   let i = m.indexOf('/');
1022   let s = m.substring(i + 1);
1023   let x = s[0].toUpperCase() + s.substring(1);
1024   for (let j = x.indexOf('/'); j >= 0; j = x.indexOf('/')) {
1025     let suffix = x.substring(j + 1)
1026     suffix = suffix[0].toUpperCase() + suffix.substring(1)
1027     let prefix = x.substring(0, j)
1028     x = prefix + suffix
1029   }
1030   if (seenNames.has(x)) {
1031     // Resolve, ResolveCodeLens, ResolveDocumentLink
1032     if (!x.startsWith('Resolve')) throw new Error(`expected Resolve, not ${x}`)
1033     x += m[0].toUpperCase() + m.substring(1, i)
1034   }
1035   seenNames.add(x);
1036   return x;
1037 }
1038
1039 // used in sig and in goReq
1040 function indirect(s: string): boolean {
1041   if (s == '' || s == 'void') return false;
1042   const skip = (x: string) => s.startsWith(x);
1043   if (skip('[]') || skip('interface') || skip('Declaration') ||
1044     skip('Definition') || skip('DocumentSelector'))
1045     return false;
1046   return true
1047 }
1048
1049 // Go signatures for methods.
1050 function sig(nm: string, a: string, b: string, names?: boolean): string {
1051   if (a.indexOf('struct') != -1) {
1052     const v = a.split('\n')
1053     extraTypes.set(`Param${nm}`, v.slice(1, v.length - 1))
1054     a = 'Param' + nm
1055   }
1056   if (a == 'void')
1057     a = '';
1058   else if (a != '') {
1059     if (names)
1060       a = ', params *' + a;
1061     else
1062       a = ', *' + a;
1063   }
1064   let ret = 'error';
1065   if (b != '' && b != 'void') {
1066     // avoid * when it is senseless
1067     if (indirect(b)) b = '*' + b;
1068     ret = `(${b}, error)`;
1069   }
1070   let start = `${nm}(`;
1071   if (names) {
1072     start = start + 'ctx ';
1073   }
1074   return `${start}context.Context${a}) ${ret}`;
1075 }
1076
1077 // write the request/notification code
1078 function output(side: side) {
1079   // make sure the output file exists
1080   if (!side.outputFile) {
1081     side.outputFile = `ts${side.name}.go`;
1082     side.fd = fs.openSync(side.outputFile, 'w');
1083   }
1084   const f = function (s: string) {
1085     fs.writeSync(side.fd, s);
1086     fs.writeSync(side.fd, '\n');
1087   };
1088   f(u.computeHeader(false));
1089   f(`
1090         import (
1091           "context"
1092           "encoding/json"
1093
1094           "golang.org/x/tools/internal/jsonrpc2"
1095           errors "golang.org/x/xerrors"
1096         )
1097         `);
1098   const a = side.name[0].toUpperCase() + side.name.substring(1)
1099   f(`type ${a} interface {`);
1100   side.methods.forEach((v) => { f(v) });
1101   f('}\n');
1102   f(`func ${side.name}Dispatch(ctx context.Context, ${side.name} ${a}, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) {
1103           switch r.Method() {`);
1104   side.cases.forEach((v) => { f(v) });
1105   f(`
1106         default:
1107           return false, nil
1108         }
1109       }`);
1110   side.calls.forEach((v) => { f(v) });
1111 }
1112
1113 // Handling of non-standard requests, so we can add gopls-specific calls.
1114 function nonstandardRequests() {
1115   server.methods.push(
1116     'NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)')
1117   server.calls.push(
1118     `func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
1119       var result interface{}
1120       if err := Call(ctx, s.Conn, method, params, &result); err != nil {
1121         return nil, err
1122       }
1123       return result, nil
1124     }
1125   `)
1126 }
1127
1128 // ----- remember it's a scripting language
1129 function main() {
1130   if (u.gitHash != u.git()) {
1131     throw new Error(
1132       `git hash mismatch, wanted\n${u.gitHash} but source is at\n${u.git()}`);
1133   }
1134   u.createOutputFiles()
1135   parse()
1136   u.printAST(program)
1137   // find the Requests and Nofificatations
1138   for (const sourceFile of program.getSourceFiles()) {
1139     if (!sourceFile.isDeclarationFile) {
1140       ts.forEachChild(sourceFile, findRPCs)
1141     }
1142   }
1143   // separate RPCs into client and server
1144   setReceives();
1145   // visit every sourceFile collecting top-level type definitions
1146   for (const sourceFile of program.getSourceFiles()) {
1147     if (!sourceFile.isDeclarationFile) {
1148       ts.forEachChild(sourceFile, genTypes)
1149     }
1150   }
1151   // check that each thing occurs exactly once, and put pointers into
1152   // seenTypes
1153   checkOnce();
1154   // for each of Client and Server there are 3 parts to the output:
1155   // 1. type X interface {methods}
1156   // 2. func (h *serverHandler) Deliver(...) { switch r.method }
1157   // 3. func (x *xDispatcher) Method(ctx, parm)
1158   not.forEach(  // notifications
1159     (v, k) => {
1160       receives.get(k) == 'client' ? goNot(client, k) : goNot(server, k)
1161     });
1162   req.forEach(  // requests
1163     (v, k) => {
1164       receives.get(k) == 'client' ? goReq(client, k) : goReq(server, k)
1165     });
1166   nonstandardRequests();
1167   // find all the types implied by seenTypes and rpcs to try to avoid
1168   // generating types that aren't used
1169   moreTypes();
1170   // and print the Go code
1171   outputTypes()
1172   console.log(`seen ${seenTypes.size + extraTypes.size}`)
1173   output(client);
1174   output(server);
1175 }
1176
1177 main()