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 }