mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
internal/lsp/protocol: bring the typescript code up to date
These versions of go.ts and requests.ts generate tsprotocol.go, tsserver.go, and tsclient.go. README.md now gives the version of the vscode source used, showing that the typescript code and tsprotocol.go are matched to the same git commit of vscode. Many of the diffs are just whitespace from vscode's formatting. Fixes golang/go#34225 Change-Id: Ib66dad9476b452f332a4c0e990faf2c6060a588e Reviewed-on: https://go-review.googlesource.com/c/tools/+/195297 Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
3720d1ec36
commit
0240832f5c
@ -3,15 +3,16 @@
|
|||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Make sure `node` is installed.
|
1. Make sure `node` is installed.
|
||||||
As explained at the [node site](<https://nodejs.org> Node)
|
As explained at the [node site](<https://nodejs.org>)
|
||||||
you may need `npm install @types/node` for the node runtime types
|
you may need `npm install @types/node` for the node runtime types
|
||||||
2. Install the typescript compiler, with `npm install typescript`.
|
2. Install the typescript compiler, with `npm install typescript`
|
||||||
3. Make sure `tsc` and `node` are in your execution path.
|
3. Make sure `tsc` and `node` are in your execution path.
|
||||||
4. Get the typescript code for the jsonrpc protocol with `git clone git@github.com:microsoft/vscode-languageserver-node.git`
|
4. Get the typescript code for the jsonrpc protocol with `git clone git@github.com:microsoft/vscode-languageserver-node.git`
|
||||||
|
5. go.ts and requests.ts, and the files they generate, are from commit 8801c20b667945f455d7e023c71d2f741caeda25
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To generated the protocol types (x/tools/internal/lsp/protocol/tsprotocol.go)
|
To generate the protocol types (x/tools/internal/lsp/protocol/tsprotocol.go)
|
||||||
```tsc go.ts && node go.js [-d dir] [-o out.go]```
|
```tsc go.ts && node go.js [-d dir] [-o out.go]```
|
||||||
|
|
||||||
and for simple checking
|
and for simple checking
|
||||||
@ -31,4 +32,4 @@ To generate the client and server boilerplate (tsclient.go and tsserver.go)
|
|||||||
|
|
||||||
## Note
|
## Note
|
||||||
|
|
||||||
`go.ts` uses the Typescript compiler's API, which is [introduced](<https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview> API) in their wiki.
|
`go.ts` and `requests.ts` use the Typescript compiler's API, which is [introduced](https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview) in their wiki.
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
// 1. Everything that returns a Go thing should have unusable?: boolean
|
// Need a general strategy for union types. This code tries (heuristically)
|
||||||
// 2. Remember what gets exported, and don't print the others (so _ can stay)
|
// to choose one, but sometimes defaults (heuristically) to interface{}
|
||||||
// 3. Merge all intersection types, and probably Heritage types too
|
|
||||||
interface Const {
|
interface Const {
|
||||||
typeName: string // repeated in each const
|
typeName: string // repeated in each const
|
||||||
goType: string
|
goType: string
|
||||||
@ -103,7 +102,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
// Ignore top-level items that produce no output
|
// Ignore top-level items that produce no output
|
||||||
if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
|
if (ts.isExpressionStatement(node) || ts.isFunctionDeclaration(node) ||
|
||||||
ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
|
ts.isImportDeclaration(node) || ts.isVariableStatement(node) ||
|
||||||
ts.isExportDeclaration(node) ||
|
ts.isExportDeclaration(node) || ts.isEmptyStatement(node) ||
|
||||||
node.kind == ts.SyntaxKind.EndOfFileToken) {
|
node.kind == ts.SyntaxKind.EndOfFileToken) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -171,6 +170,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
Structs.push(ans)
|
Structs.push(ans)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called only from doClassDeclaration
|
||||||
function fromPropDecl(node: ts.PropertyDeclaration): Field {
|
function fromPropDecl(node: ts.PropertyDeclaration): Field {
|
||||||
let id: ts.Identifier = (ts.isIdentifier(node.name) && node.name);
|
let id: ts.Identifier = (ts.isIdentifier(node.name) && node.name);
|
||||||
let opt = node.questionToken != undefined;
|
let opt = node.questionToken != undefined;
|
||||||
@ -270,6 +270,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional gen is the contents of <T>
|
||||||
function genProp(node: ts.PropertySignature, gen: ts.Identifier): Field {
|
function genProp(node: ts.PropertySignature, gen: ts.Identifier): Field {
|
||||||
let id: ts.Identifier
|
let id: ts.Identifier
|
||||||
let thing: ts.Node
|
let thing: ts.Node
|
||||||
@ -417,7 +418,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
function doTypeAlias(node: ts.TypeAliasDeclaration) {
|
function doTypeAlias(node: ts.TypeAliasDeclaration) {
|
||||||
// these are all Export Identifier alias
|
// these are all Export Identifier alias
|
||||||
let id: ts.Identifier = node.name;
|
let id: ts.Identifier = node.name;
|
||||||
let alias: ts.Node = node.type;
|
let alias: ts.TypeNode = node.type;
|
||||||
let ans = {
|
let ans = {
|
||||||
me: node,
|
me: node,
|
||||||
id: id,
|
id: id,
|
||||||
@ -427,11 +428,14 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
};
|
};
|
||||||
if (ts.isUnionTypeNode(alias)) {
|
if (ts.isUnionTypeNode(alias)) {
|
||||||
ans.goType = weirdUnionType(alias)
|
ans.goType = weirdUnionType(alias)
|
||||||
if (id.text == 'DocumentFilter')
|
|
||||||
if (ans.goType == undefined) {
|
if (ans.goType == undefined) {
|
||||||
// these are mostly redundant; maybe sort them out later
|
// these are mostly redundant; maybe sort them out later
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (ans.goType == 'interface{}') {
|
||||||
|
// we didn't know what to do, so explain the choice
|
||||||
|
ans.stuff = `// ` + getText(alias)
|
||||||
|
}
|
||||||
Types.push(ans)
|
Types.push(ans)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -441,8 +445,9 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
if (ts.isTypeReferenceNode(n)) {
|
if (ts.isTypeReferenceNode(n)) {
|
||||||
const s = toGoName(computeType(n).goType)
|
const s = toGoName(computeType(n).goType)
|
||||||
embeds.push(s)
|
embeds.push(s)
|
||||||
// It's here just for embedding, and not used independently
|
// It's here just for embedding, and not used independently, maybe
|
||||||
dontEmit.set(s, true);
|
// PJW!
|
||||||
|
// dontEmit.set(s, true); // PJW: do we need this?
|
||||||
} else
|
} else
|
||||||
throw new Error(`expected TypeRef ${strKind(n)} ${loc(n)}`)
|
throw new Error(`expected TypeRef ${strKind(n)} ${loc(n)}`)
|
||||||
})
|
})
|
||||||
@ -492,13 +497,16 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
aString = true;
|
aString = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (n.kind == ts.SyntaxKind.NumberKeyword ||
|
||||||
|
n.kind == ts.SyntaxKind.StringKeyword) {
|
||||||
|
n.kind == ts.SyntaxKind.NumberKeyword ? aNumber = true : aString = true;
|
||||||
|
return
|
||||||
|
}
|
||||||
bad = true
|
bad = true
|
||||||
})
|
})
|
||||||
if (bad) return; // none of these are useful (so far)
|
if (bad) return; // none of these are useful (so far)
|
||||||
if (aNumber) {
|
if (aNumber) {
|
||||||
if (aString)
|
if (aString) return 'interface{}';
|
||||||
throw new Error(
|
|
||||||
`weirdUnionType is both number and string ${loc(node)}`);
|
|
||||||
return 'float64';
|
return 'float64';
|
||||||
}
|
}
|
||||||
if (aString) return 'string';
|
if (aString) return 'string';
|
||||||
@ -513,6 +521,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complex and filled with heuristics
|
||||||
function computeType(node: ts.Node):
|
function computeType(node: ts.Node):
|
||||||
{goType: string, gostuff?: string, optional?: boolean, fields?: Field[]} {
|
{goType: string, gostuff?: string, optional?: boolean, fields?: Field[]} {
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
@ -540,13 +549,14 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
if (ts.isQualifiedName(tn)) {
|
if (ts.isQualifiedName(tn)) {
|
||||||
throw new Error(`qualified name at ${loc(node)}`);
|
throw new Error(`qualified name at ${loc(node)}`);
|
||||||
} else if (ts.isIdentifier(tn)) {
|
} else if (ts.isIdentifier(tn)) {
|
||||||
return {goType: tn.text};
|
return {goType: toGoName(tn.text)};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`expected identifier got ${strKind(node.typeName)} at ${loc(tn)}`)
|
`expected identifier got ${strKind(node.typeName)} at ${loc(tn)}`)
|
||||||
}
|
}
|
||||||
} else if (ts.isLiteralTypeNode(node)) {
|
} else if (ts.isLiteralTypeNode(node)) {
|
||||||
// string|float64 (are there other possibilities?)
|
// string|float64 (are there other possibilities?)
|
||||||
|
// as of 20190908: only see string
|
||||||
const txt = getText(node);
|
const txt = getText(node);
|
||||||
let typ = 'float64'
|
let typ = 'float64'
|
||||||
if (txt.charAt(0) == '\'') {
|
if (txt.charAt(0) == '\'') {
|
||||||
@ -554,6 +564,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
}
|
}
|
||||||
return {goType: typ, gostuff: getText(node)};
|
return {goType: typ, gostuff: getText(node)};
|
||||||
} else if (ts.isTypeLiteralNode(node)) {
|
} else if (ts.isTypeLiteralNode(node)) {
|
||||||
|
// {[uri:string]: TextEdit[];} -> map[string][]TextEdit
|
||||||
let x: Field[] = [];
|
let x: Field[] = [];
|
||||||
let indexCnt = 0
|
let indexCnt = 0
|
||||||
node.forEachChild((n: ts.Node) => {
|
node.forEachChild((n: ts.Node) => {
|
||||||
@ -575,13 +586,16 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
}
|
}
|
||||||
return ({goType: 'embedded!', fields: x})
|
return ({goType: 'embedded!', fields: x})
|
||||||
} else if (ts.isUnionTypeNode(node)) {
|
} else if (ts.isUnionTypeNode(node)) {
|
||||||
|
// The major heuristics
|
||||||
let x = new Array<{goType: string, gostuff?: string, optiona?: boolean}>()
|
let x = new Array<{goType: string, gostuff?: string, optiona?: boolean}>()
|
||||||
node.forEachChild((n: ts.Node) => {x.push(computeType(n))})
|
node.forEachChild((n: ts.Node) => {x.push(computeType(n))})
|
||||||
if (x.length == 2 && x[1].goType == 'nil') {
|
if (x.length == 2 && x[1].goType == 'nil') {
|
||||||
|
// Foo | null, or Foo | undefined
|
||||||
return x[0] // make it optional somehow? TODO
|
return x[0] // make it optional somehow? TODO
|
||||||
}
|
}
|
||||||
if (x[0].goType == 'bool') { // take it
|
if (x[0].goType == 'bool') { // take it, mostly
|
||||||
if (x[1].goType == 'RenameOptions') {
|
if (x[1].goType == 'RenameOptions' ||
|
||||||
|
x[1].goType == 'CodeActionOptions') {
|
||||||
return ({goType: 'interface{}', gostuff: getText(node)})
|
return ({goType: 'interface{}', gostuff: getText(node)})
|
||||||
}
|
}
|
||||||
return ({goType: 'bool', gostuff: getText(node)})
|
return ({goType: 'bool', gostuff: getText(node)})
|
||||||
@ -592,6 +606,7 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
return ({goType: 'string', gostuff: gostuff})
|
return ({goType: 'string', gostuff: gostuff})
|
||||||
}
|
}
|
||||||
if (x[0].goType == 'TextDocumentSyncOptions') {
|
if (x[0].goType == 'TextDocumentSyncOptions') {
|
||||||
|
// TextDocumentSyncOptions | TextDocumentSyncKind
|
||||||
return ({goType: 'interface{}', gostuff: gostuff})
|
return ({goType: 'interface{}', gostuff: gostuff})
|
||||||
}
|
}
|
||||||
if (x[0].goType == 'float64' && x[1].goType == 'string') {
|
if (x[0].goType == 'float64' && x[1].goType == 'string') {
|
||||||
@ -617,12 +632,13 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
throw new Error('in UnionType, weird')
|
throw new Error('in UnionType, weird')
|
||||||
} else if (ts.isParenthesizedTypeNode(node)) {
|
} else if (ts.isParenthesizedTypeNode(node)) {
|
||||||
// check that this is (TextDocumentEdit | CreateFile | RenameFile |
|
// check that this is (TextDocumentEdit | CreateFile | RenameFile |
|
||||||
// DeleteFile) TODO(pjw)
|
// DeleteFile) TODO(pjw) IT IS NOT! FIX THIS! ALSO:
|
||||||
|
// (variousOptions & StaticFegistrationOptions)
|
||||||
return {
|
return {
|
||||||
goType: 'TextDocumentEdit', gostuff: getText(node)
|
goType: 'TextDocumentEdit', gostuff: getText(node)
|
||||||
}
|
}
|
||||||
} else if (ts.isTupleTypeNode(node)) {
|
} else if (ts.isTupleTypeNode(node)) {
|
||||||
// string | [number, number]. TODO(pjw): check it really is
|
// in string | [number, number]. TODO(pjw): check it really is
|
||||||
return {
|
return {
|
||||||
goType: 'string', gostuff: getText(node)
|
goType: 'string', gostuff: getText(node)
|
||||||
}
|
}
|
||||||
@ -774,7 +790,7 @@ function emitTypes() {
|
|||||||
let stuff = (t.stuff == undefined) ? '' : t.stuff;
|
let stuff = (t.stuff == undefined) ? '' : t.stuff;
|
||||||
prgo(`// ${t.goName} is a type\n`)
|
prgo(`// ${t.goName} is a type\n`)
|
||||||
prgo(`${getComments(t.me)}`)
|
prgo(`${getComments(t.me)}`)
|
||||||
prgo(`type ${t.goName} ${t.goType}${stuff}\n`)
|
prgo(`type ${t.goName} = ${t.goType}${stuff}\n`)
|
||||||
seenConstTypes.set(t.goName, true);
|
seenConstTypes.set(t.goName, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -856,7 +872,7 @@ let byName = new Map<string, Struct>();
|
|||||||
fields.set(f.goName, x);
|
fields.set(f.goName, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fields.forEach((val) => {
|
fields.forEach((val, key) => {
|
||||||
if (val.length > 1) {
|
if (val.length > 1) {
|
||||||
// merge the fields with the same name
|
// merge the fields with the same name
|
||||||
prgo(strField(val[0], noopt, val));
|
prgo(strField(val[0], noopt, val));
|
||||||
@ -878,6 +894,7 @@ let byName = new Map<string, Struct>();
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Turn a Field into an output string
|
// Turn a Field into an output string
|
||||||
|
// flds is for merging
|
||||||
function strField(f: Field, noopt?: boolean, flds?: Field[]): string {
|
function strField(f: Field, noopt?: boolean, flds?: Field[]): string {
|
||||||
let ans: string[] = [];
|
let ans: string[] = [];
|
||||||
let opt = (!noopt && f.optional) ? '*' : ''
|
let opt = (!noopt && f.optional) ? '*' : ''
|
||||||
@ -895,6 +912,20 @@ let byName = new Map<string, Struct>();
|
|||||||
ans.push(`\t${f.goName} ${opt}${f.goType} ${f.json}${stuff}\n`)
|
ans.push(`\t${f.goName} ${opt}${f.goType} ${f.json}${stuff}\n`)
|
||||||
}
|
}
|
||||||
else if (flds !== undefined) {
|
else if (flds !== undefined) {
|
||||||
|
// The logic that got us here is imprecise, so it is possible that
|
||||||
|
// the fields are really all the same, and don't need to be
|
||||||
|
// combined into a struct.
|
||||||
|
let simple = true;
|
||||||
|
for (const ff of flds) {
|
||||||
|
if (ff.substruct !== undefined || byName.get(ff.goType) !== undefined) {
|
||||||
|
simple = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (simple) {
|
||||||
|
// should check that the ffs are really all the same
|
||||||
|
return strField(flds[0], noopt)
|
||||||
|
}
|
||||||
ans.push(`\t${f.goName} ${opt}struct{\n`);
|
ans.push(`\t${f.goName} ${opt}struct{\n`);
|
||||||
for (const ff of flds) {
|
for (const ff of flds) {
|
||||||
if (ff.substruct !== undefined) {
|
if (ff.substruct !== undefined) {
|
||||||
@ -926,8 +957,9 @@ let byName = new Map<string, Struct>();
|
|||||||
// need the consts too! Generate modifying prefixes and suffixes to ensure
|
// need the consts too! Generate modifying prefixes and suffixes to ensure
|
||||||
// consts are unique. (Go consts are package-level, but Typescript's are
|
// consts are unique. (Go consts are package-level, but Typescript's are
|
||||||
// not.) Use suffixes to minimize changes to gopls.
|
// not.) Use suffixes to minimize changes to gopls.
|
||||||
let pref = new Map<string, string>(
|
let pref = new Map<string, string>([
|
||||||
[['DiagnosticSeverity', 'Severity'], ['WatchKind', 'Watch']]) // typeName->prefix
|
['DiagnosticSeverity', 'Severity'], ['WatchKind', 'Watch']
|
||||||
|
]) // typeName->prefix
|
||||||
let suff = new Map<string, string>([
|
let suff = new Map<string, string>([
|
||||||
['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat']
|
['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat']
|
||||||
])
|
])
|
||||||
|
@ -29,6 +29,7 @@ function prb(s: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let program: ts.Program;
|
let program: ts.Program;
|
||||||
|
|
||||||
function generate(files: string[], options: ts.CompilerOptions): void {
|
function generate(files: string[], options: ts.CompilerOptions): void {
|
||||||
program = ts.createProgram(files, options);
|
program = ts.createProgram(files, options);
|
||||||
program.getTypeChecker();
|
program.getTypeChecker();
|
||||||
@ -62,15 +63,11 @@ function generate(files: string[], options: ts.CompilerOptions): void {
|
|||||||
// 2. func (h *serverHandler) Deliver(...) { switch r.method }
|
// 2. func (h *serverHandler) Deliver(...) { switch r.method }
|
||||||
// 3. func (x *xDispatcher) Method(ctx, parm)
|
// 3. func (x *xDispatcher) Method(ctx, parm)
|
||||||
not.forEach(
|
not.forEach(
|
||||||
(v, k) => {
|
(v, k) => {receives.get(k) == 'client' ? goNot(client, k) :
|
||||||
receives.get(k) == 'client' ? goNot(client, k) :
|
goNot(server, k)});
|
||||||
goNot(server, k)
|
|
||||||
});
|
|
||||||
req.forEach(
|
req.forEach(
|
||||||
(v, k) => {
|
(v, k) => {receives.get(k) == 'client' ? goReq(client, k) :
|
||||||
receives.get(k) == 'client' ? goReq(client, k) :
|
goReq(server, k)});
|
||||||
goReq(server, k)
|
|
||||||
});
|
|
||||||
// and print the Go code
|
// and print the Go code
|
||||||
output(client);
|
output(client);
|
||||||
output(server);
|
output(server);
|
||||||
@ -104,7 +101,7 @@ const notNil = `if r.Params != nil {
|
|||||||
// Go code for notifications. Side is client or server, m is the request method
|
// Go code for notifications. Side is client or server, m is the request method
|
||||||
function goNot(side: side, m: string) {
|
function goNot(side: side, m: string) {
|
||||||
const n = not.get(m);
|
const n = not.get(m);
|
||||||
let a = goType(m, n.typeArguments[0]);
|
let a = goType(side, m, n.typeArguments[0]);
|
||||||
// let b = goType(m, n.typeArguments[1]); These are registration options
|
// let b = goType(m, n.typeArguments[1]); These are registration options
|
||||||
const nm = methodName(m);
|
const nm = methodName(m);
|
||||||
side.methods.push(sig(nm, a, ''));
|
side.methods.push(sig(nm, a, ''));
|
||||||
@ -140,8 +137,8 @@ function goReq(side: side, m: string) {
|
|||||||
const n = req.get(m);
|
const n = req.get(m);
|
||||||
|
|
||||||
const nm = methodName(m);
|
const nm = methodName(m);
|
||||||
let a = goType(m, n.typeArguments[0]);
|
let a = goType(side, m, n.typeArguments[0]);
|
||||||
let b = goType(m, n.typeArguments[1]);
|
let b = goType(side, m, n.typeArguments[1]);
|
||||||
if (n.getText().includes('Type0')) {
|
if (n.getText().includes('Type0')) {
|
||||||
b = a;
|
b = a;
|
||||||
a = ''; // workspace/workspaceFolders and shutdown
|
a = ''; // workspace/workspaceFolders and shutdown
|
||||||
@ -214,7 +211,7 @@ function methodName(m: string): string {
|
|||||||
function output(side: side) {
|
function output(side: side) {
|
||||||
if (side.outputFile === undefined) side.outputFile = `ts${side.name}.go`;
|
if (side.outputFile === undefined) side.outputFile = `ts${side.name}.go`;
|
||||||
side.fd = fs.openSync(side.outputFile, 'w');
|
side.fd = fs.openSync(side.outputFile, 'w');
|
||||||
const f = function (s: string) {
|
const f = function(s: string) {
|
||||||
fs.writeSync(side.fd, s);
|
fs.writeSync(side.fd, s);
|
||||||
fs.writeSync(side.fd, '\n');
|
fs.writeSync(side.fd, '\n');
|
||||||
};
|
};
|
||||||
@ -231,9 +228,10 @@ function output(side: side) {
|
|||||||
`);
|
`);
|
||||||
const a = side.name[0].toUpperCase() + side.name.substring(1)
|
const a = side.name[0].toUpperCase() + side.name.substring(1)
|
||||||
f(`type ${a} interface {`);
|
f(`type ${a} interface {`);
|
||||||
side.methods.forEach((v) => { f(v) });
|
side.methods.forEach((v) => {f(v)});
|
||||||
f('}\n');
|
f('}\n');
|
||||||
f(`func (h ${side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
|
f(`func (h ${
|
||||||
|
side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
|
||||||
if delivered {
|
if delivered {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -246,7 +244,7 @@ function output(side: side) {
|
|||||||
}
|
}
|
||||||
r.Conn().Cancel(params.ID)
|
r.Conn().Cancel(params.ID)
|
||||||
return true`);
|
return true`);
|
||||||
side.cases.forEach((v) => { f(v) });
|
side.cases.forEach((v) => {f(v)});
|
||||||
f(`
|
f(`
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@ -257,7 +255,7 @@ function output(side: side) {
|
|||||||
*jsonrpc2.Conn
|
*jsonrpc2.Conn
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
side.calls.forEach((v) => { f(v) });
|
side.calls.forEach((v) => {f(v)});
|
||||||
if (side.name == 'server')
|
if (side.name == 'server')
|
||||||
f(`
|
f(`
|
||||||
type CancelParams struct {
|
type CancelParams struct {
|
||||||
@ -266,31 +264,46 @@ function output(side: side) {
|
|||||||
*/
|
*/
|
||||||
ID jsonrpc2.ID \`json:"id"\`
|
ID jsonrpc2.ID \`json:"id"\`
|
||||||
}`);
|
}`);
|
||||||
|
f(`// Types constructed to avoid structs as formal argument types`)
|
||||||
|
side.ourTypes.forEach((val, key) => f(`type ${val} ${key}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface side {
|
interface side {
|
||||||
methods: string[];
|
methods: string[];
|
||||||
cases: string[];
|
cases: string[];
|
||||||
calls: string[];
|
calls: string[];
|
||||||
|
ourTypes: Map<string, string>;
|
||||||
name: string; // client or server
|
name: string; // client or server
|
||||||
goName: string; // Client or Server
|
goName: string; // Client or Server
|
||||||
outputFile?: string;
|
outputFile?: string;
|
||||||
fd?: number
|
fd?: number
|
||||||
}
|
}
|
||||||
let client: side =
|
let client: side = {
|
||||||
{ methods: [], cases: [], calls: [], name: 'client', goName: 'Client' };
|
methods: [],
|
||||||
let server: side =
|
cases: [],
|
||||||
{ methods: [], cases: [], calls: [], name: 'server', goName: 'Server' };
|
calls: [],
|
||||||
|
name: 'client',
|
||||||
|
goName: 'Client',
|
||||||
|
ourTypes: new Map<string, string>()
|
||||||
|
};
|
||||||
|
let server: side = {
|
||||||
|
methods: [],
|
||||||
|
cases: [],
|
||||||
|
calls: [],
|
||||||
|
name: 'server',
|
||||||
|
goName: 'Server',
|
||||||
|
ourTypes: new Map<string, string>()
|
||||||
|
};
|
||||||
|
|
||||||
let req = new Map<string, ts.NewExpression>(); // requests
|
let req = new Map<string, ts.NewExpression>(); // requests
|
||||||
let not = new Map<string, ts.NewExpression>(); // notifications
|
let not = new Map<string, ts.NewExpression>(); // notifications
|
||||||
let receives = new Map<string, 'server' | 'client'>(); // who receives it
|
let receives = new Map<string, 'server'|'client'>(); // who receives it
|
||||||
|
|
||||||
function setReceives() {
|
function setReceives() {
|
||||||
// mark them all as server, then adjust the client ones.
|
// mark them all as server, then adjust the client ones.
|
||||||
// it would be nice to have some independent check
|
// it would be nice to have some independent check
|
||||||
req.forEach((_, k) => { receives.set(k, 'server') });
|
req.forEach((_, k) => {receives.set(k, 'server')});
|
||||||
not.forEach((_, k) => { receives.set(k, 'server') });
|
not.forEach((_, k) => {receives.set(k, 'server')});
|
||||||
receives.set('window/logMessage', 'client');
|
receives.set('window/logMessage', 'client');
|
||||||
receives.set('telemetry/event', 'client');
|
receives.set('telemetry/event', 'client');
|
||||||
receives.set('client/registerCapability', 'client');
|
receives.set('client/registerCapability', 'client');
|
||||||
@ -308,12 +321,12 @@ function setReceives() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function goType(m: string, n: ts.Node): string {
|
function goType(side: side, m: string, n: ts.Node): string {
|
||||||
if (n === undefined) return '';
|
if (n === undefined) return '';
|
||||||
if (ts.isTypeReferenceNode(n)) return n.typeName.getText();
|
if (ts.isTypeReferenceNode(n)) return n.typeName.getText();
|
||||||
if (n.kind == ts.SyntaxKind.VoidKeyword) return '';
|
if (n.kind == ts.SyntaxKind.VoidKeyword) return '';
|
||||||
if (n.kind == ts.SyntaxKind.AnyKeyword) return 'interface{}';
|
if (n.kind == ts.SyntaxKind.AnyKeyword) return 'interface{}';
|
||||||
if (ts.isArrayTypeNode(n)) return '[]' + goType(m, n.elementType);
|
if (ts.isArrayTypeNode(n)) return '[]' + goType(side, m, n.elementType);
|
||||||
// special cases, before we get confused
|
// special cases, before we get confused
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case 'textDocument/completion':
|
case 'textDocument/completion':
|
||||||
@ -328,7 +341,8 @@ function goType(m: string, n: ts.Node): string {
|
|||||||
if (ts.isUnionTypeNode(n)) {
|
if (ts.isUnionTypeNode(n)) {
|
||||||
let x: string[] = [];
|
let x: string[] = [];
|
||||||
n.types.forEach(
|
n.types.forEach(
|
||||||
(v) => { v.kind != ts.SyntaxKind.NullKeyword && x.push(goType(m, v)) });
|
(v) => {v.kind != ts.SyntaxKind.NullKeyword &&
|
||||||
|
x.push(goType(side, m, v))});
|
||||||
if (x.length == 1) return x[0];
|
if (x.length == 1) return x[0];
|
||||||
|
|
||||||
prb(`===========${m} ${x}`)
|
prb(`===========${m} ${x}`)
|
||||||
@ -338,6 +352,28 @@ function goType(m: string, n: ts.Node): string {
|
|||||||
if (x[1] == '[]' + x[0] + 'Link') return x[1];
|
if (x[1] == '[]' + x[0] + 'Link') return x[1];
|
||||||
throw new Error(`${m}, ${x} unexpected types`)
|
throw new Error(`${m}, ${x} unexpected types`)
|
||||||
}
|
}
|
||||||
|
if (ts.isIntersectionTypeNode(n)) {
|
||||||
|
// we expect only TypeReferences, and put out a struct with embedded types
|
||||||
|
// This is not good, as it uses a struct where a type name ought to be.
|
||||||
|
let x: string[] = [];
|
||||||
|
n.types.forEach((v) => {
|
||||||
|
// expect only TypeReferences
|
||||||
|
if (!ts.isTypeReferenceNode(v)) {
|
||||||
|
throw new Error(
|
||||||
|
`expected only TypeReferences in Intersection ${getText(n)}`)
|
||||||
|
}
|
||||||
|
x.push(goType(side, m, v));
|
||||||
|
x.push(';')
|
||||||
|
})
|
||||||
|
x.push('}')
|
||||||
|
let ans = 'struct {'.concat(...x);
|
||||||
|
// If ans does not have a type, create it
|
||||||
|
if (side.ourTypes.get(ans) == undefined) {
|
||||||
|
side.ourTypes.set(ans, 'Param' + getText(n).substring(0, 6))
|
||||||
|
}
|
||||||
|
// Return the type
|
||||||
|
return side.ourTypes.get(ans)
|
||||||
|
}
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +405,14 @@ function genStuff(node: ts.Node) {
|
|||||||
v.set(s, node);
|
v.set(s, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find the text of a node
|
||||||
|
function getText(node: ts.Node): string {
|
||||||
|
let sf = node.getSourceFile();
|
||||||
|
let start = node.getStart(sf)
|
||||||
|
let end = node.getEnd()
|
||||||
|
return sf.text.substring(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
function lookUp(n: ts.NewExpression): ts.NodeArray<ts.TypeNode> {
|
function lookUp(n: ts.NewExpression): ts.NodeArray<ts.TypeNode> {
|
||||||
// parent should be VariableDeclaration. its children should be
|
// parent should be VariableDeclaration. its children should be
|
||||||
// Identifier('type') ???
|
// Identifier('type') ???
|
||||||
@ -473,7 +517,7 @@ function main() {
|
|||||||
}
|
}
|
||||||
createOutputFiles()
|
createOutputFiles()
|
||||||
generate(
|
generate(
|
||||||
files, { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS });
|
files, {target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS});
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user