UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

275 lines (270 loc) 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.helpCommand = void 0; exports.masterScriptArgs = masterScriptArgs; exports.getReplCommands = getReplCommands; exports.getCommandNames = getCommandNames; exports.getCommand = getCommand; exports.asOptionName = asOptionName; exports.longestCommandName = longestCommandName; exports.padCmd = padCmd; const repl_quit_1 = require("./repl-quit"); const execute_1 = require("../execute"); const prompt_1 = require("../prompt"); const repl_version_1 = require("./repl-version"); const repl_parse_1 = require("./repl-parse"); const repl_execute_1 = require("./repl-execute"); const repl_normalize_1 = require("./repl-normalize"); const repl_dataflow_1 = require("./repl-dataflow"); const repl_cfg_1 = require("./repl-cfg"); const ansi_1 = require("../../../util/text/ansi"); const args_1 = require("../../../util/text/args"); const assert_1 = require("../../../util/assert"); const scripts_info_1 = require("../../common/scripts-info"); const repl_query_1 = require("./repl-query"); const repl_signature_1 = require("./repl-signature"); const version_1 = require("../../../util/version"); const cmd = (name, f) => (0, ansi_1.color)(name, 6 /* Colors.Cyan */, f, { style: 1 /* FontStyles.Bold */ }); const ansiSgr = new RegExp(String.fromCharCode(27) + '\\[[0-9;]*m', 'g'); const visibleLength = (s) => s.replace(ansiSgr, '').length; /** Appends `content` after `prefix`, word-wrapping to the terminal width with a hanging indent. */ function wrapAfter(prefix, content) { const width = process.stdout.isTTY ? (process.stdout.columns ?? 80) : Infinity; const indent = visibleLength(prefix); const pad = ' '.repeat(indent); const lines = []; let line = ''; let lineLength = indent; for (const word of content.split(' ')) { const wordLength = visibleLength(word); if (line !== '' && lineLength + 1 + wordLength > width) { lines.push(line); line = word; lineLength = indent + wordLength; } else { line = line === '' ? word : `${line} ${word}`; lineLength = line === word ? indent + wordLength : lineLength + 1 + wordLength; } } if (line !== '') { lines.push(line); } return prefix + lines.map((l, i) => i === 0 ? l : pad + l).join('\n'); } function printHelpForScript(script, f, starredVersion) { let content = script[1].description; if (starredVersion) { content += ` ${(0, ansi_1.italic)(`(star: ${starredVersion.description})`, f)}`; } const aliases = script[1].aliases; if (aliases.length > 0) { content += ` ${(0, ansi_1.italic)(`(alias${aliases.length > 1 ? 'es' : ''}:`, f)} ${aliases.map(a => cmd(':' + a, f)).join(', ')}${(0, ansi_1.italic)(')', f)}`; } return wrapAfter(` ${cmd(padCmd(':' + script[0] + (starredVersion ? '[*]' : '')), f)}`, content); } function printFamilyVariants(children, cmds, f) { const variant = (name) => { const aliases = cmds[name].aliases; const suffix = aliases.length > 0 ? ' ' + (0, ansi_1.italic)(`(${aliases.map(a => ':' + a).join(', ')})`, f) : ''; return cmd(':' + name + (cmds[name + '*'] ? '[*]' : ''), f) + suffix; }; return wrapAfter(` ${(0, ansi_1.italic)('variants:', f)} `, children.map(variant).join(', ')); } function printCommandHelp(formatter) { const cmds = getReplCommands(); const bases = Object.entries(cmds).filter(([name, c]) => !c.script && !name.endsWith('*')); const names = bases.map(([name]) => name); const shortestPrefixCommand = (name) => names.filter(n => name.startsWith(n)).reduce((a, b) => a.length <= b.length ? a : b); const children = new Map(); for (const name of names) { const root = shortestPrefixCommand(name); if (root !== name) { children.set(root, [...children.get(root) ?? [], name]); } } const lines = []; for (const entry of bases.filter(([name]) => shortestPrefixCommand(name) === name).sort((a, b) => a[0].localeCompare(b[0]))) { lines.push(printHelpForScript(entry, formatter, cmds[entry[0] + '*'])); const variants = children.get(entry[0]); if (variants) { lines.push(printFamilyVariants(variants.sort(), cmds, formatter)); } } return lines.join('\n'); } exports.helpCommand = { description: 'Show help information', isCodeCommand: false, script: false, usageExample: ':help', aliases: ['h', '?'], fn: ({ output }) => { initCommandMapping(); output.stdout(` If enabled ('--r-session-access' and if using the 'r-shell' engine), you can just enter R expressions which get evaluated right away: ${prompt_1.rawPrompt} ${(0, ansi_1.bold)('1 + 1', output.formatter)} ${(0, ansi_1.italic)('[1] 2', output.formatter)} Besides that, you can use the following commands. The scripts ${(0, ansi_1.italic)('can', output.formatter)} accept further arguments. In general, those ending with [*] may be called with and without the star. There are the following basic commands: ${printCommandHelp(output.formatter)} Furthermore, you can directly call the following scripts which accept arguments. If you are unsure, try to add ${(0, ansi_1.italic)('--help', output.formatter)} after the command. ${Array.from(Object.entries(getReplCommands())).filter(([, { script }]) => script).map(([command, { description }]) => wrapAfter(` ${cmd(padCmd(':' + command), output.formatter)}`, description)).sort().join('\n')} You can combine commands by separating them with a semicolon ${(0, ansi_1.bold)(';', output.formatter)}. Commands that accept a file path support two path prefixes: ${(0, ansi_1.color)('file://<path>', 2 /* Colors.Green */, output.formatter, { style: 1 /* FontStyles.Bold */ })} run the command once on the given file or folder ${(0, ansi_1.color)('watch://<path>', 3 /* Colors.Yellow */, output.formatter, { style: 1 /* FontStyles.Bold */ })} re-run the command whenever the file (or any file in the folder) changes Press Ctrl+C or enter any other command to leave watch mode. You are running flowR ${(0, ansi_1.bold)('v' + (0, version_1.flowrVersion)().toString(), output.formatter)} (use ${(0, ansi_1.bold)(':version', output.formatter)} for details). Check for newer releases and per-install upgrade steps (Docker, npm, source) at: ${(0, ansi_1.color)('https://github.com/flowr-analysis/flowr/releases', 6 /* Colors.Cyan */, output.formatter, { style: 1 /* FontStyles.Bold */ })} `); } }; /** * All commands that should be available in the REPL. */ const _commands = { 'help': exports.helpCommand, 'quit': repl_quit_1.quitCommand, 'version': repl_version_1.versionCommand, 'execute': repl_execute_1.executeCommand, 'parse': repl_parse_1.parseCommand, 'normalize': repl_normalize_1.normalizeCommand, 'normalize*': repl_normalize_1.normalizeStarCommand, 'normalize#': repl_normalize_1.normalizeHashCommand, 'dataflow': repl_dataflow_1.dataflowCommand, 'dataflow*': repl_dataflow_1.dataflowStarCommand, 'dataflowsimple': repl_dataflow_1.dataflowSimplifiedCommand, 'dataflowsimple*': repl_dataflow_1.dataflowSimpleStarCommand, 'dataflowascii': repl_dataflow_1.dataflowAsciiCommand, 'dataflowsilent': repl_dataflow_1.dataflowSilentCommand, 'controlflow': repl_cfg_1.controlflowCommand, 'controlflow*': repl_cfg_1.controlflowStarCommand, 'controlflowbb': repl_cfg_1.controlflowBbCommand, 'controlflowbb*': repl_cfg_1.controlflowBbStarCommand, 'query': repl_query_1.queryCommand, 'query*': repl_query_1.queryStarCommand, 'signature': repl_signature_1.signatureCommand }; let commandsInitialized = false; function hasModule(path) { try { require.resolve(path); return true; } catch { return false; } } /** * The args a master script is forked with: `remainingLine` tokenized, plus `--config-json <analyzer.flowrConfig>` * so it sees the repl's current (possibly `:query \@config`-edited) config -- unless the script does not support * that option, or the user already named a config themselves. */ function masterScriptArgs(remainingLine, config, scriptOptions) { const args = (0, args_1.splitAtEscapeSensitive)(remainingLine); if (scriptOptions.some(o => o.name === 'config-json') && !args.includes('--config-json') && !args.includes('--config-file')) { args.push('--config-json', JSON.stringify(config)); } return args; } /** * Retrieve all REPL commands (including those generated from master scripts) */ function getReplCommands() { if (commandsInitialized) { return _commands; } for (const [script, { target, description, type, options }] of Object.entries(scripts_info_1.scripts)) { if (type === 'master script') { _commands[script] = { description, aliases: [], script: true, usageExample: `:${script} --help`, isCodeCommand: false, fn: async ({ output, remainingLine, analyzer }) => { // check if the target *module* exists in the current directory, else try two dirs up, otherwise, fail with a message let path = `${__dirname}/${target}`; if (!hasModule(path)) { path = `${__dirname}/../../${target}`; if (!hasModule(path)) { output.stderr(`Could not find the target script ${target} in the current directory or two directories up.`); return; } } await (0, execute_1.waitOnScript)(path, masterScriptArgs(remainingLine, analyzer.flowrConfig, options), stdio => (0, execute_1.stdioCaptureProcessor)(stdio, msg => output.stdout(msg), msg => output.stderr(msg))); } }; } } commandsInitialized = true; return _commands; } /** * The names of all commands including their aliases (but without the leading `:`) */ function getCommandNames() { if (commandNames === undefined) { initCommandMapping(); } return commandNames; } let commandNames = undefined; // maps command names or aliases to the actual command name let commandMapping = undefined; function initCommandMapping() { const mapping = {}; const names = []; for (const [command, { aliases }] of Object.entries(getReplCommands())) { (0, assert_1.guard)(mapping[command] === undefined, `Command ${command} is already registered!`); mapping[command] = command; for (const alias of aliases) { (0, assert_1.guard)(mapping[alias] === undefined, `Command (alias) ${alias} is already registered!`); mapping[alias] = command; } names.push(command); names.push(...aliases); } commandMapping = mapping; commandNames = names; } /** * Get the command for a given command name or alias. * @param command - The name of the command (without the leading `:`) */ function getCommand(command) { if (commandMapping === undefined) { initCommandMapping(); } return getReplCommands()[commandMapping[command]]; } /** * Formats the given argument name as a command line option (with single or double dashes). */ function asOptionName(argument) { if (argument.length == 1) { return `-${argument}`; } else { return `--${argument}`; } } let _longestCommandName = undefined; /** * Retrieve the length of the longest command name (including star and brackets if applicable) */ function longestCommandName() { if (_longestCommandName === undefined) { _longestCommandName = Array.from(Object.keys(getReplCommands()), k => k.endsWith('*') ? k.length + 3 : k.length).reduce((p, n) => Math.max(p, n), 0); } return _longestCommandName; } /** * Pad the given command string to the length of the longest command name plus two spaces. * @see {@link longestCommandName} */ function padCmd(string) { return String(string).padEnd(longestCommandName() + 2, ' '); } //# sourceMappingURL=repl-commands.js.map