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';
6 // This file contains various utilities having to do with producing strings
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`
17 export const gitHash = '901fd40345060d159f07d234bbc967966a929a34'
18 let outFname = 'tsprotocol.go';
19 let fda: number, fdb: number, fde: number; // file descriptors
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
26 export function pra(s: string) {
27 return (fs.writeSync(fda, s))
29 export function prb(s: string) {
30 return (fs.writeSync(fdb, s))
32 export function prgo(s: string) {
33 return (fs.writeSync(fde, s))
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);
47 if (a.substring(0, 5) == 'ref: ') {
48 const fname = `${dir}${srcDir}/.git/` + a.substring(5);
49 let b = fs.readFileSync(fname).toString()
51 return b.substring(0, 40);
54 throw new Error('failed to find the git commit hash')
57 // Produce a header for Go output files
58 export function computeHeader(pkgDoc: boolean): string {
61 for (const f of fnames) {
62 const st = fs.statSync(f)
63 if (st.mtimeMs > lastMod) {
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`
83 // Turn a typescript name into an exportable Go name, and appease lint
84 export function goName(s: string): string {
86 if (s.charAt(0) == '_') {
87 ans = 'Inner' + s.substring(1)
89 else { ans = s.substring(0, 1).toUpperCase() + s.substring(1) };
90 ans = ans.replace(/Uri$/, 'URI')
91 ans = ans.replace(/Id$/, 'ID')
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' : ''}"\``;
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']
115 if (pref.get(type)) ans = pref.get(type) + ans;
116 if (suff.has(type)) ans = ans + suff.get(type)
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)
130 // --------- printing the AST, for debugging
132 export function printAST(program: ts.Program) {
133 // dump the ast, for debugging
134 const f = function (n: ts.Node) {
137 for (const sourceFile of program.getSourceFiles()) {
138 if (!sourceFile.isDeclarationFile) {
139 // walk the tree to do stuff
140 ts.forEachChild(sourceFile, f);
144 for (const key of Object.keys(seenThings).sort()) {
145 pra(`${key}: ${seenThings[key]} \n`)
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)
155 function describe(node: ts.Node, pr: (s: string) => any) {
156 if (node === undefined) {
161 function f(n: ts.Node) {
163 if (ts.isIdentifier(n)) {
164 pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
166 else if (ts.isPropertySignature(n) || ts.isEnumMember(n)) {
167 pra(`${indent} ${loc(n)} ${strKind(n)} \n`)
169 else if (ts.isTypeLiteralNode(n)) {
171 pr(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`)
173 else if (ts.isStringLiteral(n)) {
174 pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`)
176 else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`) };
178 ts.forEachChild(n, f)
179 indent = indent.slice(0, indent.length - 2)
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)
193 const n = fn.search(/-node./)
194 fn = fn.substring(n + 6)
195 return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
198 // --- various string stuff
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)
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
212 export function strKind(n: ts.Node): string {
213 if (n == null || n == undefined) {
216 const x = ts.SyntaxKind[n.kind];
217 // some of these have two names
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';
232 return 'QualifiedName';
233 case 'FirstTemplateToken':
234 return 'NoSubstitutionTemplateLiteral';
235 case 'LastTemplateToken':
236 return 'TemplateTail';
237 case 'FirstTypeNode':
238 return 'TypePredicate';