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';
7 // This file contains various utilities having to do with producing strings
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`
18 export const gitHash = 'dae62de921d25964e8732411ca09e532dde992f5';
19 let outFname = 'tsprotocol.go';
20 let fda: number, fdb: number, fde: number; // file descriptors
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
27 export function pra(s: string) {
28 return (fs.writeSync(fda, s));
30 export function prb(s: string) {
31 return (fs.writeSync(fdb, s));
33 export function prgo(s: string) {
34 return (fs.writeSync(fde, s));
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);
48 if (a.substring(0, 5) == 'ref: ') {
49 const fname = `${dir}${srcDir}/.git/` + a.substring(5);
50 let b = fs.readFileSync(fname).toString();
52 return b.substring(0, 40);
55 throw new Error('failed to find the git commit hash');
58 // Produce a header for Go output files
59 export function computeHeader(pkgDoc: boolean): string {
62 for (const f of fnames) {
63 const st = fs.statSync(f);
64 if (st.mtimeMs > lastMod) {
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.
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';
82 return cp + a + b + c;
85 return cp + b + a + c;
89 // Turn a typescript name into an exportable Go name, and appease lint
90 export function goName(s: string): string {
92 if (s.charAt(0) == '_') {
93 ans = 'Inner' + s.substring(1);
95 else { ans = s.substring(0, 1).toUpperCase() + s.substring(1); }
96 ans = ans.replace(/Uri$/, 'URI');
97 ans = ans.replace(/Id$/, 'ID');
101 // Generate JSON tag for a struct field
102 export function JSON(n: ts.PropertySignature): string {
103 const json = `\`json:"${n.name.getText()}${
104 n.questionToken != undefined ? ',omitempty' : ''}"\``;
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'],
122 if (pref.get(type)) ans = pref.get(type) + ans;
123 if (suff.has(type)) ans = ans + suff.get(type);
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);
137 // --------- printing the AST, for debugging
139 export function printAST(program: ts.Program) {
140 // dump the ast, for debugging
141 const f = function (n: ts.Node) {
144 for (const sourceFile of program.getSourceFiles()) {
145 if (!sourceFile.isDeclarationFile) {
146 // walk the tree to do stuff
147 ts.forEachChild(sourceFile, f);
151 for (const key of Object.keys(seenThings).sort()) {
152 pra(`${key}: ${seenThings[key]} \n`);
156 // Used in printing the AST
157 let seenThings = new Map<string, number>();
158 function seenAdd(x: string) {
159 seenThings[x] = (seenThings[x] === undefined ? 1 : seenThings[x] + 1);
162 // eslint-disable-next-line no-unused-vars
163 function describe(node: ts.Node, pr: (s: string) => any) {
164 if (node === undefined) {
169 function f(n: ts.Node) {
171 if (ts.isIdentifier(n)) {
172 pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`);
174 else if (ts.isPropertySignature(n) || ts.isEnumMember(n)) {
175 pra(`${indent} ${loc(n)} ${strKind(n)} \n`);
177 else if (ts.isTypeLiteralNode(n)) {
179 pr(`${indent} ${loc(n)} ${strKind(n)} ${m.length} \n`);
181 else if (ts.isStringLiteral(n)) {
182 pr(`${indent} ${loc(n)} ${strKind(n)} ${n.text} \n`);
184 else { pr(`${indent} ${loc(n)} ${strKind(n)} \n`); }
186 ts.forEachChild(n, f);
187 indent = indent.slice(0, indent.length - 2);
193 // For debugging, say where an AST node is in a file
194 export function loc(node: ts.Node): string {
195 const sf = node.getSourceFile();
196 const start = node.getStart();
197 const x = sf.getLineAndCharacterOfPosition(start);
198 const full = node.getFullStart();
199 const y = sf.getLineAndCharacterOfPosition(full);
200 let fn = sf.fileName;
201 const n = fn.search(/-node./);
202 fn = fn.substring(n + 6);
203 return `${fn} ${x.line + 1}: ${x.character + 1} (${y.line + 1}: ${
206 // --- various string stuff
208 // return a string of the kinds of the immediate descendants
209 // as part of printing the AST tree
210 function kinds(n: ts.Node): string {
211 let res = 'Seen ' + strKind(n);
212 function f(n: ts.Node): void { res += ' ' + strKind(n); }
213 ts.forEachChild(n, f);
217 // What kind of AST node is it? This would just be typescript's
218 // SyntaxKind[n.kind] except that the default names for some nodes
220 export function strKind(n: ts.Node): string {
221 if (n == null || n == undefined) {
224 return kindToStr(n.kind);
227 export function kindToStr(k: ts.SyntaxKind): string {
228 if (k === undefined) return 'unDefined';
229 const x = ts.SyntaxKind[k];
230 // some of these have two names
234 case 'FirstAssignment':
235 return 'EqualsToken';
236 case 'FirstBinaryOperator':
237 return 'LessThanToken';
238 case 'FirstCompoundAssignment':
239 return 'PlusEqualsToken';
240 case 'FirstContextualKeyword':
241 return 'AbstractKeyword';
242 case 'FirstLiteralToken':
243 return 'NumericLiteral';
245 return 'QualifiedName';
246 case 'FirstTemplateToken':
247 return 'NoSubstitutionTemplateLiteral';
248 case 'LastTemplateToken':
249 return 'TemplateTail';
250 case 'FirstTypeNode':
251 return 'TypePredicate';