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