github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/protocol/typescript/code.ts (about)

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