github.com/jd-ly/tools@v0.5.7/internal/lsp/protocol/typescript/util.ts (about)

     1  
     2  // for us typescript ignorati, having an import makes this file a module
     3  import * as fs from 'fs';
     4  import * as ts from 'typescript';
     5  
     6  // This file contains various utilities having to do with producing strings
     7  // and managing output
     8  
     9  // ------ create files
    10  let dir = process.env['HOME'];
    11  const srcDir = '/vscode-languageserver-node'
    12  export const fnames = [
    13    `${dir}${srcDir}/protocol/src/common/protocol.ts`,
    14    `${dir}/${srcDir}/protocol/src/browser/main.ts`, `${dir}${srcDir}/types/src/main.ts`,
    15    `${dir}${srcDir}/jsonrpc/src/node/main.ts`
    16  ];
    17  export const gitHash = '901fd40345060d159f07d234bbc967966a929a34'
    18  let outFname = 'tsprotocol.go';
    19  let fda: number, fdb: number, fde: number;  // file descriptors
    20  
    21  export function createOutputFiles() {
    22    fda = fs.openSync('/tmp/ts-a', 'w')  // dump of AST
    23    fdb = fs.openSync('/tmp/ts-b', 'w')  // unused, for debugging
    24    fde = fs.openSync(outFname, 'w')     // generated Go
    25  }
    26  export function pra(s: string) {
    27    return (fs.writeSync(fda, s))
    28  }
    29  export function prb(s: string) {
    30    return (fs.writeSync(fdb, s))
    31  }
    32  export function prgo(s: string) {
    33    return (fs.writeSync(fde, s))
    34  }
    35  
    36  // Get the hash value of the git commit
    37  export function git(): string {
    38    let a = fs.readFileSync(`${dir}${srcDir}/.git/HEAD`).toString();
    39    // ref: refs/heads/foo, or a hash like
    40    // cc12d1a1c7df935012cdef5d085cdba04a7c8ebe
    41    if (a.charAt(a.length - 1) == '\n') {
    42      a = a.substring(0, a.length - 1);
    43    }
    44    if (a.length == 40) {
    45      return a  // a hash
    46    }
    47    if (a.substring(0, 5) == 'ref: ') {
    48      const fname = `${dir}${srcDir}/.git/` + a.substring(5);
    49      let b = fs.readFileSync(fname).toString()
    50      if (b.length == 41) {
    51        return b.substring(0, 40);
    52      }
    53    }
    54    throw new Error('failed to find the git commit hash')
    55  }
    56  
    57  // Produce a header for Go output files
    58  export function computeHeader(pkgDoc: boolean): string {
    59    let lastMod = 0
    60    let lastDate: Date
    61    for (const f of fnames) {
    62      const st = fs.statSync(f)
    63      if (st.mtimeMs > lastMod) {
    64        lastMod = st.mtimeMs
    65        lastDate = st.mtime
    66      }
    67    }
    68    const a =
    69      `// Package protocol contains data types and code for LSP jsonrpcs\n` +
    70      `// generated automatically from vscode-languageserver-node\n` +
    71      `// commit: ${gitHash}\n` +
    72      `// last fetched ${lastDate}\n`
    73    const b = 'package protocol\n'
    74    const c = `\n// Code generated (see typescript/README.md) DO NOT EDIT.\n\n`
    75    if (pkgDoc) {
    76      return a + b + c
    77    }
    78    else {
    79      return b + a + c
    80    }
    81  };
    82  
    83  // Turn a typescript name into an exportable Go name, and appease lint
    84  export function goName(s: string): string {
    85    let ans = s
    86    if (s.charAt(0) == '_') {
    87      ans = 'Inner' + s.substring(1)
    88    }
    89    else { ans = s.substring(0, 1).toUpperCase() + s.substring(1) };
    90    ans = ans.replace(/Uri$/, 'URI')
    91    ans = ans.replace(/Id$/, 'ID')
    92    return ans
    93  }
    94  
    95  // Generate JSON tag for a struct field
    96  export function JSON(n: ts.PropertySignature): string {
    97    const json = `\`json:"${n.name.getText()}${
    98      n.questionToken != undefined ? ',omitempty' : ''}"\``;
    99    return json
   100  }
   101  
   102  // Generate modifying prefixes and suffixes to ensure
   103  // consts are unique. (Go consts are package-level, but Typescript's are
   104  // not.) Use suffixes to minimize changes to gopls.
   105  export function constName(nm: string, type: string): string {
   106    let pref = new Map<string, string>([
   107      ['DiagnosticSeverity', 'Severity'], ['WatchKind', 'Watch'],
   108      ['SignatureHelpTriggerKind', 'Sig'], ['CompletionItemTag', 'Compl']
   109    ])  // typeName->prefix
   110    let suff = new Map<string, string>([
   111      ['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat'],
   112      ['SymbolTag', 'Symbol']
   113    ])
   114    let ans = nm;
   115    if (pref.get(type)) ans = pref.get(type) + ans;
   116    if (suff.has(type)) ans = ans + suff.get(type)
   117    return ans
   118  }
   119  
   120  // Find the comments associated with an AST node
   121  export function getComments(node: ts.Node): string {
   122    const sf = node.getSourceFile();
   123    const start = node.getStart(sf, false)
   124    const starta = node.getStart(sf, true)
   125    const x = sf.text.substring(starta, start)
   126    return x
   127  }
   128  
   129  
   130  // --------- printing the AST, for debugging
   131  
   132  export function printAST(program: ts.Program) {
   133    // dump the ast, for debugging
   134    const f = function (n: ts.Node) {
   135      describe(n, pra)
   136    };
   137    for (const sourceFile of program.getSourceFiles()) {
   138      if (!sourceFile.isDeclarationFile) {
   139        // walk the tree to do stuff
   140        ts.forEachChild(sourceFile, f);
   141      }
   142    }
   143    pra('\n')
   144    for (const key of Object.keys(seenThings).sort()) {
   145      pra(`${key}: ${seenThings[key]} \n`)
   146    }
   147  }
   148  
   149  // Used in printing the AST
   150  let seenThings = new Map<string, number>();
   151  function seenAdd(x: string) {
   152    seenThings[x] = (seenThings[x] === undefined ? 1 : seenThings[x] + 1)
   153  }
   154  
   155  function describe(node: ts.Node, pr: (s: string) => any) {
   156    if (node === undefined) {
   157      return
   158    }
   159    let indent = '';
   160  
   161    function f(n: ts.Node) {
   162      seenAdd(kinds(n))
   163      if (ts.isIdentifier(n)) {
   164        pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
   165      }
   166      else if (ts.isPropertySignature(n) || ts.isEnumMember(n)) {
   167        pra(`${indent} ${loc(n)} ${strKind(n)} \n`)
   168      }
   169      else if (ts.isTypeLiteralNode(n)) {
   170        let m = n.members
   171        pr(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`)
   172      }
   173      else if (ts.isStringLiteral(n)) {
   174        pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
   175      }
   176      else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`) };
   177      indent += ' .'
   178      ts.forEachChild(n, f)
   179      indent = indent.slice(0, indent.length - 2)
   180    }
   181    f(node)
   182  }
   183  
   184  
   185  // For debugging, say where an AST node is in a file
   186  export function loc(node: ts.Node): string {
   187    const sf = node.getSourceFile();
   188    const start = node.getStart()
   189    const x = sf.getLineAndCharacterOfPosition(start)
   190    const full = node.getFullStart()
   191    const y = sf.getLineAndCharacterOfPosition(full)
   192    let fn = sf.fileName
   193    const n = fn.search(/-node./)
   194    fn = fn.substring(n + 6)
   195    return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
   196      y.character + 1})`
   197  }
   198  // --- various string stuff
   199  
   200  // return a string of the kinds of the immediate descendants
   201  // as part of printing the AST tree
   202  function kinds(n: ts.Node): string {
   203    let res = 'Seen ' + strKind(n);
   204    function f(n: ts.Node): void { res += ' ' + strKind(n) };
   205    ts.forEachChild(n, f)
   206    return res
   207  }
   208  
   209  // What kind of AST node is it? This would just be typescript's
   210  // SyntaxKind[n.kind] except that the default names for some nodes
   211  // are misleading
   212  export function strKind(n: ts.Node): string {
   213    if (n == null || n == undefined) {
   214      return 'null'
   215    }
   216    const x = ts.SyntaxKind[n.kind];
   217    // some of these have two names
   218    switch (x) {
   219      default:
   220        return x;
   221      case 'FirstAssignment':
   222        return 'EqualsToken';
   223      case 'FirstBinaryOperator':
   224        return 'LessThanToken';
   225      case 'FirstCompoundAssignment':
   226        return 'PlusEqualsToken';
   227      case 'FirstContextualKeyword':
   228        return 'AbstractKeyword';
   229      case 'FirstLiteralToken':
   230        return 'NumericLiteral';
   231      case 'FirstNode':
   232        return 'QualifiedName';
   233      case 'FirstTemplateToken':
   234        return 'NoSubstitutionTemplateLiteral';
   235      case 'LastTemplateToken':
   236        return 'TemplateTail';
   237      case 'FirstTypeNode':
   238        return 'TypePredicate';
   239    }
   240  }