@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
185 lines (163 loc) • 8.21 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegisteredQueries = void 0;
exports.showQuery = showQuery;
exports.registerQueryDocumentation = registerQueryDocumentation;
exports.sliceQueryShorthand = sliceQueryShorthand;
exports.getQueryTitle = getQueryTitle;
exports.getPageNameForQuery = getPageNameForQuery;
exports.linkToQueryOfName = linkToQueryOfName;
exports.tocForQueryType = tocForQueryType;
exports.queryPages = queryPages;
exports.assertAllQueriesDocumented = assertAllQueriesDocumented;
const query_1 = require("../../queries/query");
const virtual_queries_1 = require("../../queries/virtual-query/virtual-queries");
const doc_auto_gen_1 = require("./doc-auto-gen");
const doc_structure_1 = require("./doc-structure");
const assert_1 = require("../../util/assert");
const path_1 = __importDefault(require("path"));
const json_1 = require("../../util/json");
const ansi_1 = require("../../util/text/ansi");
const doc_files_1 = require("./doc-files");
const doc_dfg_1 = require("./doc-dfg");
const doc_code_1 = require("./doc-code");
const time_1 = require("../../util/text/time");
const query_print_1 = require("../../queries/query-print");
const flowr_analyzer_builder_1 = require("../../project/flowr-analyzer-builder");
const flowr_file_1 = require("../../project/context/flowr-file");
const doc_cli_option_1 = require("./doc-cli-option");
const QueryDocFile = 'src/documentation/wiki-query.ts';
/**
* Visualizes a query and its results in markdown format.
*/
async function showQuery(parser, code, queries, { showCode, collapseResult, collapseQuery, shorthand, ctx, files } = {}) {
const now = performance.now();
const analyzer = await new flowr_analyzer_builder_1.FlowrAnalyzerBuilder().setParser(parser).build();
for (const file of files ?? []) {
analyzer.addFile(new flowr_file_1.FlowrInlineTextFile(file.name, file.content));
}
analyzer.addRequest(code);
const results = await analyzer.query(queries);
const duration = performance.now() - now;
const metaInfo = `
The analysis required _${(0, time_1.printAsMs)(duration)}_ (including parsing and normalization and the query) within the generation environment.
`.trim();
const str = JSON.stringify(queries, json_1.jsonReplacer, collapseQuery ? ' ' : 2);
return `
${(0, doc_code_1.codeBlock)('json', collapseQuery ? str.split('\n').join(' ').replace(/([{[])\s{2,}/g, '$1 ').replace(/\s{2,}([\]}])/g, ' $1') : str)}
${(function () {
if ((queries.length === 1 && Object.keys(queries[0]).length === 1) || shorthand) {
return `(This can be shortened to \`@${queries[0].type}${shorthand ? ' ' + shorthand : ''}\` when used with the REPL command ${(0, doc_cli_option_1.getReplCommand)('query')}).`;
}
else {
return '';
}
})()}
${collapseResult ? ' <details> <summary style="color:gray">Show Results</summary>' : ''}
_Results (prettified and summarized):_
${await (0, query_print_1.asciiSummaryOfQueryResult)(ansi_1.markdownFormatter, duration, results, analyzer, queries)}
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
${metaInfo}
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
Please consult the ${ctx ? ctx.linkPage('wiki/Interface', 'Interface') : `[Interface](${doc_files_1.FlowrWikiBaseRef}/Interface)`} wiki page for more information on how to get those.
${(0, doc_code_1.jsonWithLimit)(results)}
</details>
${showCode ? `
<details> <summary style="color:gray">Original Code</summary>
${await (0, doc_dfg_1.printDfGraphForCode)(parser, code, { switchCodeAndGraph: true })}
</details>
` : ''}
${collapseResult ? '</details>' : ''}
`;
}
exports.RegisteredQueries = {
'active': new Map(),
'virtual': new Map()
};
/**
* Registers a new documentation for a query.
*/
function registerQueryDocumentation(query, doc) {
const map = exports.RegisteredQueries[doc.type];
if (map.has(query)) {
throw new Error(`Query ${query} already registered`);
}
map.set(query, doc);
}
/**
* Creates a REPL shorthand for the given slicing criteria and R code (`f` requests a forward slice,
* `i` inlines resolvable `source()` calls into the reconstruction; the flags may be combined as `fi`).
*/
function sliceQueryShorthand(criteria, code, forward, inline) {
return `(${(criteria.join(';'))})${forward ? 'f' : ''}${inline ? 'i' : ''} "${code}"`;
}
/** The display name of a query, from its definition or, for a virtual query, its documentation. */
function getQueryTitle(id) {
const title = query_1.SupportedQueries[id]?.title
?? exports.RegisteredQueries.virtual.get(id)?.name;
(0, assert_1.guard)(title !== undefined, () => `Query ${id} is not documented!`);
return title;
}
/** Mirrors the `[Linting Rule] ...` pages, see `getPageNameForLintingRule`. */
function getPageNameForQuery(id) {
return (0, query_1.queryWikiPage)(getQueryTitle(id));
}
function queryPageUrl(id) {
return `${doc_files_1.FlowrWikiBaseRef}/${encodeURIComponent(getPageNameForQuery(id).replaceAll(' ', '-'))}`;
}
/** `linkText` defaults to the query's title. */
function linkToQueryOfName(id, linkText) {
return `[${linkText ?? getQueryTitle(id)}](${queryPageUrl(id)})`;
}
/**
*
*/
function tocForQueryType(type) {
const queries = [...exports.RegisteredQueries[type].keys()].sort((a, b) => getQueryTitle(a).localeCompare(getQueryTitle(b)));
const result = [];
for (const id of queries) {
result.push(`1. ${linkToQueryOfName(id)} (\`${id}\`):\\\n ${exports.RegisteredQueries[type].get(id)?.shortDescription}`);
}
return result.join('\n');
}
async function explainQuery(shell, ctx, id, { shortDescription, functionName, functionFile, buildExplanation }) {
const name = getQueryTitle(id);
const syntax = query_1.SupportedQueries[id]?.syntax;
return `
${(0, doc_auto_gen_1.autoGenHeader)({ filename: QueryDocFile, purpose: 'query API' })}
${(0, doc_structure_1.section)(name + ` <sup>[<a href="${doc_files_1.FlowrWikiBaseRef}/Query-API">overview</a>]</sup>`, 2, name)}
${shortDescription}\\
_This query is requested with the type \`${id}\`._${syntax ? `\\\nRun in the REPL: \`:query ${syntax}\`` : ''}
${await buildExplanation(shell, ctx)}
<details>
<summary style="color:gray">Implementation Details</summary>
Responsible for the execution of the ${name} query is \`${functionName}\` in ${(0, doc_files_1.getFilePathMd)(functionFile)}.
</details>
`.trim();
}
/** Keyed by the file path each page is written to. */
async function queryPages(shell, ctx, type) {
const result = {};
for (const [id, doc] of exports.RegisteredQueries[type].entries()) {
result[path_1.default.join('wiki', `${getPageNameForQuery(id)}.md`)] = await explainQuery(shell, ctx, id, doc);
}
return result;
}
/** Without this, a new query would silently end up without a wiki page. */
function assertAllQueriesDocumented() {
const undocumented = [
...Object.keys(query_1.SupportedQueries).filter(q => !exports.RegisteredQueries.active.has(q)),
...Object.keys(virtual_queries_1.SupportedVirtualQueries).filter(q => !exports.RegisteredQueries.virtual.has(q))
];
(0, assert_1.guard)(undocumented.length === 0, () => `The quer${undocumented.length === 1 ? 'y' : 'ies'} ${undocumented.map(q => `'${q}'`).join(', ')} ${undocumented.length === 1 ? 'has' : 'have'} no documentation! `
+ `Please add a 'registerQueryDocumentation' entry in ${QueryDocFile}.`);
const stale = [
...[...exports.RegisteredQueries.active.keys()].filter(q => !(q in query_1.SupportedQueries)),
...[...exports.RegisteredQueries.virtual.keys()].filter(q => !(q in virtual_queries_1.SupportedVirtualQueries))
];
(0, assert_1.guard)(stale.length === 0, () => `The documentation of ${stale.map(q => `'${q}'`).join(', ')} refers to (a) query type(s) that no longer exist(s).`);
}
//# sourceMappingURL=doc-query.js.map