UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

193 lines 8.74 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.replCompleter = replCompleter; exports.makeDefaultReplReadline = makeDefaultReplReadline; exports.replProcessAnswer = replProcessAnswer; exports.repl = repl; exports.loadReplHistory = loadReplHistory; /** * Basically a helper file to allow the main 'flowr' script (located in the source root) to provide its repl * * @module */ const prompt_1 = require("./prompt"); const readline = __importStar(require("readline")); const repl_execute_1 = require("./commands/repl-execute"); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const args_1 = require("../../util/text/args"); const repl_commands_1 = require("./commands/repl-commands"); const scripts_info_1 = require("../common/scripts-info"); const retriever_1 = require("../../r-bridge/retriever"); const repl_main_1 = require("./commands/repl-main"); const shell_1 = require("../../r-bridge/shell"); const log_1 = require("../../util/log"); let _replCompleterKeywords = undefined; function replCompleterKeywords() { if (_replCompleterKeywords === undefined) { _replCompleterKeywords = Array.from((0, repl_commands_1.getCommandNames)(), s => `:${s}`); } return _replCompleterKeywords; } const defaultHistoryFile = path_1.default.join(os_1.default.tmpdir(), '.flowrhistory'); /** * Used by the repl to provide automatic completions for a given (partial) input line */ function replCompleter(line) { const splitLine = (0, args_1.splitAtEscapeSensitive)(line); // did we just type a space (and are starting a new arg right now)? const startingNewArg = line.endsWith(' '); // if we typed a command fully already, autocomplete the arguments if (splitLine.length > 1 || startingNewArg) { const commandNameColon = replCompleterKeywords().find(k => splitLine[0] === k); if (commandNameColon) { const completions = []; const commandName = commandNameColon.slice(1); if ((0, repl_commands_1.getCommand)(commandName)?.script === true) { // autocomplete script arguments const options = scripts_info_1.scripts[commandName].options; completions.push(...(0, scripts_info_1.getValidOptionsForCompletion)(options, splitLine).map(o => `${o} `)); } else { // autocomplete command arguments (specifically, autocomplete the file:// protocol) completions.push(retriever_1.fileProtocol); } // add an empty option so that it doesn't autocomplete the only defined option immediately completions.push(' '); const currentArg = startingNewArg ? '' : splitLine[splitLine.length - 1]; return [completions.filter(a => a.startsWith(currentArg)), currentArg]; } } // if no command is already typed, just return all commands that match return [replCompleterKeywords().filter(k => k.startsWith(line)).map(k => `${k} `), line]; } function makeDefaultReplReadline() { return { input: process.stdin, output: process.stdout, tabSize: 4, terminal: true, history: loadReplHistory(defaultHistoryFile), removeHistoryDuplicates: true, completer: replCompleter }; } ; async function replProcessStatement(output, statement, parser, allowRSessionAccess) { if (statement.startsWith(':')) { const command = statement.slice(1).split(' ')[0].toLowerCase(); const processor = (0, repl_commands_1.getCommand)(command); const bold = (s) => output.formatter.format(s, { style: 1 /* FontStyles.Bold */ }); if (processor) { try { await processor.fn(output, parser, statement.slice(command.length + 2).trim(), allowRSessionAccess); } catch (e) { output.stdout(`${bold(`Failed to execute command ${command}`)}: ${e?.message}. Using the ${bold('--verbose')} flag on startup may provide additional information.\n`); if (log_1.log.settings.minLevel < 6 /* LogLevel.Fatal */) { console.error(e); } } } else { output.stdout(`the command '${command}' is unknown, try ${bold(':help')} for more information\n`); } } else { await (0, repl_execute_1.tryExecuteRShellCommand)(output, parser, statement, allowRSessionAccess); } } /** * This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics). * * @param output - Defines two methods that every function in the repl uses to output its data. * @param expr - The expression to process. * @param parser - The {@link RShell} or {@link TreeSitterExecutor} to use (see {@link repl}). * @param allowRSessionAccess - If true, allows the execution of arbitrary R code. */ async function replProcessAnswer(output, expr, parser, allowRSessionAccess) { const statements = (0, args_1.splitAtEscapeSensitive)(expr, false, ';'); for (const statement of statements) { await replProcessStatement(output, statement, parser, allowRSessionAccess); } } /** * Provides a never-ending repl (read-evaluate-print loop) processor that can be used to interact with a {@link RShell} as well as all flowR scripts. * * The repl allows for two kinds of inputs: * - Starting with a colon `:`, indicating a command (probe `:help`, and refer to {@link commands}) </li> * - Starting with anything else, indicating default R code to be directly executed. If you kill the underlying shell, that is on you! </li> * * @param options - The options for the repl. See {@link FlowrReplOptions} for more information. * * For the execution, this function makes use of {@link replProcessAnswer}. * */ async function repl({ parser = new shell_1.RShell({ revive: 2 /* RShellReviveOptions.Always */ }), rl = readline.createInterface(makeDefaultReplReadline()), output = repl_main_1.standardReplOutput, historyFile = defaultHistoryFile, allowRSessionAccess = false }) { if (historyFile) { rl.on('history', h => fs_1.default.writeFileSync(historyFile, h.join('\n'), { encoding: 'utf-8' })); } // the incredible repl :D, we kill it with ':quit' while (true) { await new Promise((resolve, reject) => { rl.question((0, prompt_1.prompt)(), answer => { rl.pause(); replProcessAnswer(output, answer, parser, allowRSessionAccess).then(() => { rl.resume(); resolve(); }).catch(reject); }); }); } } function loadReplHistory(historyFile) { try { if (!fs_1.default.existsSync(historyFile)) { return undefined; } return fs_1.default.readFileSync(historyFile, { encoding: 'utf-8' }).split('\n'); } catch (e) { log_1.log.error(`Failed to load repl history from ${historyFile}: ${e?.message}`); log_1.log.error(e?.stack); return undefined; } } //# sourceMappingURL=core.js.map