UNPKG

plaxtony

Version:

Static code analysis of SC2 Galaxy Script

346 lines 13.6 kB
"use strict"; 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