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