in internal/lsp/protocol/typescript/code.ts [861:979]
function goUnionType(n: ts.UnionTypeNode, nm: string): string {
let help = `/*${n.getText()}*/`; // show the original as a comment
// There are some bad cases with newlines:
// range?: boolean | {\n };
// full?: boolean | {\n /**\n * The server supports deltas for full documents.\n */\n delta?: boolean;\n }
// These are handled specially:
if (nm == 'range') help = help.replace(/\n/, '');
if (nm == 'full' && help.indexOf('\n') != -1) {
help = '/*boolean | <elided struct>*/';
}
// handle all the special cases
switch (n.types.length) {
case 2: {
const a = strKind(n.types[0]);
const b = strKind(n.types[1]);
if (a == 'NumberKeyword' && b == 'StringKeyword') { // ID
return `interface{} ${help}`;
}
// for null, b is not useful (LiternalType)
if (n.types[1].getText() === 'null') {
if (nm == 'textDocument/codeAction') {
// (Command | CodeAction)[] | null
return `[]CodeAction ${help}`;
}
let v = goType(n.types[0], 'a');
return `${v} ${help}`;
}
if (a == 'BooleanKeyword') { // usually want bool
if (nm == 'codeActionProvider') return `interface{} ${help}`;
if (nm == 'renameProvider') return `interface{} ${help}`;
if (nm == 'full') return `interface{} ${help}`; // there's a struct
if (nm == 'save') return `${goType(n.types[1], '680')} ${help}`;
return `${goType(n.types[0], 'b')} ${help}`;
}
if (b == 'ArrayType') return `${goType(n.types[1], 'c')} ${help}`;
if (help.includes('InsertReplaceEdit') && n.types[0].getText() == 'TextEdit') {
return `*TextEdit ${help}`;
}
if (a == 'TypeReference') {
if (nm == 'edits') return `${goType(n.types[0], '901')} ${help}`;
if (a == b) return `interface{} ${help}`;
if (nm == 'code') return `interface{} ${help}`;
if (nm == 'editRange') return `${goType(n.types[0], '904')} ${help}`;
if (nm === 'location') return `${goType(n.types[0], '905')} ${help}`;
}
if (a == 'StringKeyword') return `string ${help}`;
if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
return `${goType(n.types[0], nm)}`;
}
if (a == 'TypeLiteral' && b === 'TypeLiteral') {
// DocumentDiagnosticReport
// the first one includes the second one
return `${goType(n.types[0], '9d')}`;
}
throw new Error(`911 ${nm}: a:${a}/${goType(n.types[0], '9a')} b:${b}/${goType(n.types[1], '9b')} ${loc(n)}`);
}
case 3: {
const aa = strKind(n.types[0]);
const bb = strKind(n.types[1]);
const cc = strKind(n.types[2]);
if (nm === 'workspace/symbol') return `${goType(n.types[0], '930')} ${help}`;
if (nm == 'DocumentFilter' || nm == 'NotebookDocumentFilter' || nm == 'TextDocumentFilter') {
// not really a union. the first is enough, up to a missing
// omitempty but avoid repetitious comments
return `${goType(n.types[0], 'g')}`;
}
if (nm == 'textDocument/documentSymbol') {
return `[]interface{} ${help}`;
}
if (aa == 'TypeReference' && bb == 'ArrayType' && (cc == 'NullKeyword' || cc === 'LiteralType')) {
return `${goType(n.types[0], 'd')} ${help}`;
}
if (aa == 'TypeReference' && bb == aa && cc == 'ArrayType') {
// should check that this is Hover.Contents
return `${goType(n.types[0], 'e')} ${help}`;
}
if (aa == 'ArrayType' && bb == 'TypeReference' && (cc == 'NullKeyword' || cc === 'LiteralType')) {
// check this is nm == 'textDocument/completion'
return `${goType(n.types[1], 'f')} ${help}`;
}
if (aa == 'LiteralType' && bb == aa && cc == aa) return `string ${help}`;
// keep this for diagnosing unexpected interface{} results
// console.log(`931, interface{} for ${aa}/${goType(n.types[0], 'g')},${bb}/${goType(n.types[1], 'h')},${cc}/${goType(n.types[2], 'i')} ${nm}`);
break;
}
case 4:
if (nm == 'documentChanges') return `TextDocumentEdit ${help} `;
if (nm == 'textDocument/prepareRename') return `Range ${help} `;
break;
case 8: // LSPany
break;
default:
throw new Error(`957 goUnionType len=${n.types.length} nm=${nm} ${n.getText()}`);
}
// Result will be interface{} with a comment
let isLiteral = true;
let literal = 'string';
let res = 'interface{} /* ';
n.types.forEach((v: ts.TypeNode, i: number) => {
// might get an interface inside:
// (Command | CodeAction)[] | null
let m = goType(v, nm);
if (m.indexOf('interface') != -1) {
// avoid nested comments
m = m.split(' ')[0];
}
m = m.split('\n').join('; '); // sloppy: struct{;
res = res.concat(`${i == 0 ? '' : ' | '}`, m);
if (!ts.isLiteralTypeNode(v)) isLiteral = false;
else literal = strKind(v.literal) == 'StringLiteral' ? 'string' : 'number';
});
if (!isLiteral) {
return res + '*/';
}
// I don't think we get here
// trace?: 'off' | 'messages' | 'verbose' should get string
return `${literal} /* ${n.getText()} */`;
}