plaxtony
Version:
Static code analysis of SC2 Galaxy Script
346 lines • 13.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDocumentationOfSymbol = exports.S2WorkspaceMetadata = void 0;
const utils_1 = require("../compiler/utils");
const trig = require("../sc2mod/trigger");
const dtypes = require("../sc2mod/dtypes");
const utils_2 = require("./utils");
const common_1 = require("../common");
const elementNotValidCharsRE = /[^a-zA-Z0-9_]+/g;
const elementValidCharsRE = /^[a-z][a-z0-9_]*$/i;
const quationMarkRE = /"/g;
const tildeRE = /~/g;
class S2WorkspaceMetadata {
constructor(workspace, metadataCfg, dataCatalogConfig) {
this.workspace = workspace;
this.metadataCfg = metadataCfg;
this.dataCatalogConfig = dataCatalogConfig;
this.symbolMap = new Map();
this.presetValueParentMap = new Map();
}
getElementSymbolName(el) {
let parts = [];
let elemName = '';
if (el.name) {
elemName = el.name;
}
else {
const localizedName = this.workspace.locComponent.triggers.elementName('Name', el);
if (localizedName) {
elemName = localizedName.replace(elementNotValidCharsRE, '');
}
}
if (el instanceof trig.FunctionDef && (el.flags & 2 /* Native */ || el.flags & 16384 /* NoScriptPrefix */)) {
parts.push(elemName);
}
else if (el.value && (el.value.startsWith('c_') ||
el.value === 'null' ||
el.value === 'true' ||
el.value === 'false')) {
parts.push(el.value);
}
else {
if (el.libId) {
switch (el.constructor) {
case trig.FunctionDef:
case trig.Preset:
{
parts.push('lib' + el.libId);
break;
}
}
}
if (el instanceof trig.FunctionDef) {
if (el.flags & 512 /* Operator */)
parts.push('op');
else
parts.push('gf');
}
else if (el instanceof trig.Preset) {
parts.push('ge');
}
if (parts.length || el.constructor === trig.PresetValue) {
parts.push(elemName);
}
else {
parts.push(elemName.charAt(0).toLowerCase() + elemName.substr(1));
}
}
return parts.join('_');
}
mapContainer(container) {
for (const el of container.getElements().values()) {
if (el instanceof trig.FunctionDef) {
if (el.flags & 32 /* Template */)
continue;
this.symbolMap.set(this.getElementSymbolName(el), el);
}
else if (el instanceof trig.Preset) {
if (!(el.flags & 64 /* PresetGenConstVar */) && !(el.flags & 128 /* PresetCustom */))
continue;
if (el.baseType === 'bool')
continue;
for (const presetRef of el.values) {
const presetValue = presetRef.resolve();
this.presetValueParentMap.set(presetValue.link(), el);
const pname = this.getNameOfPresetValue(el, presetValue);
if (!pname || pname === 'null' || !pname.match(elementValidCharsRE)) {
continue;
}
if (this.symbolMap.has(pname)) {
// logger.warn(
// `Already exists: "${pname}"`,
// [el.name, presetValue.name, presetValue.value],
// [this.symbolMap.get(pname)]
// );
continue;
}
this.symbolMap.set(pname, presetValue);
}
}
}
}
async build() {
this.workspace.locComponent.lang = this.metadataCfg.localization;
this.workspace.metadataArchives = this.workspace.allArchives.filter(item => {
switch (this.metadataCfg.loadLevel) {
case 'Default':
{
return true;
}
case 'Builtin':
{
return item.isBuiltin;
}
case 'Core':
{
return item.name === 'mods/core.sc2mod';
}
case 'None':
default:
{
return false;
}
}
});
common_1.logger.info('metadata archives', ...this.workspace.metadataArchives.map(item => item.name));
const loaders = [];
loaders.push(this.workspace.trigComponent.load());
loaders.push(this.workspace.locComponent.load());
if (this.dataCatalogConfig.enabled) {
loaders.push(this.workspace.catalogComponent.load());
}
await Promise.all(loaders);
for (const lib of this.workspace.trigComponent.getStore().getLibraries().values()) {
this.mapContainer(lib);
}
this.mapContainer(this.workspace.trigComponent.getStore());
}
findElementByName(name) {
return this.symbolMap.get(name);
}
findPresetDef(presetValue) {
return this.presetValueParentMap.get(presetValue.link());
}
getNameOfPresetValue(preset, presetValue) {
if (preset.baseType === 'bool')
return;
if (preset.flags & 128 /* PresetCustom */) {
return presetValue.value;
}
else {
return this.getElementSymbolName(preset) + '_' + this.getElementSymbolName(presetValue);
}
}
getConstantNamesOfPreset(preset) {
let names = [];
for (const link of preset.values) {
const presetValue = link.resolve();
const tmp = this.getNameOfPresetValue(preset, presetValue);
if (tmp) {
names.push(tmp);
}
}
return names;
}
getParameterTypeDoc(el) {
let typeName;
let type;
if (el.type === 'gamelink') {
type = `${el.type}<${(el.gameType || 'any')}>`;
}
else if (el.type === 'preset') {
typeName = this.workspace.locComponent.triggers.elementName('Name', el.typeElement.resolve());
type = `Preset<${this.getElementSymbolName(el.typeElement.resolve())}>`;
}
else {
type = el.type;
}
return { typeName, type };
}
getParamDoc(el) {
const name = this.workspace.locComponent.triggers.elementName('Name', el);
const type = this.getParameterTypeDoc(el.type).type;
return { name, type };
}
getElementDoc(el, extended) {
let name = '**' + this.workspace.locComponent.triggers.elementName('Name', el) + '**';
if (el instanceof trig.FunctionDef) {
if (extended) {
const grammar = this.workspace.locComponent.triggers.elementName('Grammar', el);
if (grammar) {
name += ' (' + grammar.replace(tildeRE, '`') + ')';
}
}
if (el.flags & 131072 /* Restricted */) {
name += '\n\n__Blizzard only__';
}
const hint = this.workspace.locComponent.triggers.elementName('Hint', el);
if (hint) {
name += '\n\n' + hint.replace(quationMarkRE, '*');
}
return name;
}
else if (el instanceof trig.PresetValue) {
const presetName = this.workspace.locComponent.triggers.elementName('Name', this.findPresetDef(el));
return name + (presetName ? ' - ' + presetName : '');
}
else if (el instanceof trig.ParamDef) {
let type;
if (el.type.type === 'gamelink') {
type = '`gamelink<' + (el.type.gameType || 'any') + '>`';
}
else if (el.type.type === 'preset') {
type = '' + this.workspace.locComponent.triggers.elementName('Name', el.type.typeElement.resolve()) + '';
}
else {
type = '`' + el.type.type + '`';
}
return name + ' - ' + type + '';
}
else {
return name;
}
}
getSymbolDoc(symbolName, extended = true) {
const el = this.findElementByName(symbolName);
if (!el)
return null;
return this.getElementDoc(el, extended);
}
getFunctionArgumentsDoc(symbolName) {
const el = this.findElementByName(symbolName);
if (!el)
return null;
const docs = [];
if (el.flags & 16 /* Event */) {
docs.push('**Trigger**');
}
for (const param of el.getParameters()) {
docs.push(this.getElementDoc(param, false));
}
return docs;
}
getElementTypeOfNode(node) {
// if (node.kind !== gt.SyntaxKind.StringLiteral) return null;
if (node.parent.kind !== 121 /* CallExpression */)
return null;
const callExpr = node.parent;
if (callExpr.expression.kind !== 113 /* Identifier */)
return null;
const el = this.findElementByName(callExpr.expression.name);
if (!el)
return null;
let index = null;
if (node.kind === 12 /* CommaToken */ || node.kind === 6 /* OpenParenToken */) {
for (const [key, token] of callExpr.syntaxTokens.entries()) {
index = key - 1;
if (node.end < token.end) {
break;
}
}
}
else {
for (const [key, arg] of callExpr.arguments.entries()) {
if (arg === node) {
index = key;
break;
}
}
}
if (index === null)
return null;
if (el.flags & 16 /* Event */) {
index--;
}
if (el.getParameters().length <= index || index < 0)
return null;
return el.getParameters()[index].type;
}
getGameLinkItem(gameType, id) {
const family = dtypes.S2DataCatalogDomain[gameType];
let results = Array
.from(this.workspace.catalogComponent.getStore().findEntry(family))
.map(x => Array.from(x))
.flat();
if (id) {
results = results.filter(x => x.id === id);
}
return new Set(results);
}
getGameLinkDetails(entity) {
return this.workspace.resolvePath(entity.uri);
}
getGameLinkLocalizedName(gameType, gameLink, includePrefix = false) {
var _a;
const name = ((_a = this.workspace.locComponent.strings.get('Game').text(`${gameType}/Name/${gameLink}`)) !== null && _a !== void 0 ? _a : this.workspace.locComponent.strings.get('Object').text(`${gameType}/Name/${gameLink}`));
if (!includePrefix) {
return name;
}
if (!name) {
return undefined;
}
const prefix = this.workspace.locComponent.strings.get('Object').text(`${gameType}/EditorPrefix/${gameLink}`);
const suffix = this.workspace.locComponent.strings.get('Object').text(`${gameType}/EditorSuffix/${gameLink}`);
return (prefix ? prefix + ' ' : '') + (name !== null && name !== void 0 ? name : gameLink) + (suffix ? ' ' + suffix : '');
}
}
__decorate([
common_1.logIt()
], S2WorkspaceMetadata.prototype, "build", null);
exports.S2WorkspaceMetadata = S2WorkspaceMetadata;
function getDocumentationOfSymbol(store, symbol, extended = true) {
if (store.s2metadata) {
const r = store.s2metadata.getSymbolDoc(symbol.escapedName, extended);
if (r)
return r;
}
for (const decl of symbol.declarations) {
const sourceFile = utils_1.getSourceFileOfNode(decl);
const linesTxt = [];
let currLine = decl.line;
if (!sourceFile.commentsLineMap.has(currLine)) {
--currLine;
}
while (currLine > 0 && sourceFile.commentsLineMap.has(currLine)) {
const ctoken = sourceFile.commentsLineMap.get(currLine);
const cpos = utils_2.getLineAndCharacterOfPosition(sourceFile, ctoken.pos);
if (ctoken.line !== decl.line && cpos.character > 0)
break;
linesTxt.push(sourceFile.text.substring(ctoken.pos + 2, ctoken.end));
--currLine;
}
if (linesTxt.length) {
return linesTxt.reverse().map((line) => line.replace(/^ /, '')).join(' \n');
}
}
return null;
}
exports.getDocumentationOfSymbol = getDocumentationOfSymbol;
//# sourceMappingURL=s2meta.js.map