@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
174 lines (170 loc) • 7.93 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const data_1 = require("../r-bridge/data/data");
const log_1 = require("../../test/functionality/_helper/log");
const doc_auto_gen_1 = require("./doc-util/doc-auto-gen");
const strings_1 = require("../util/text/strings");
const doc_general_1 = require("./doc-util/doc-general");
const tree_sitter_executor_1 = require("../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor");
const fs_1 = __importDefault(require("fs"));
const doc_structure_1 = require("./doc-util/doc-structure");
const detailedInfoFile = 'coverage/flowr-test-details.json';
function obtainDetailedInfos() {
if (!fs_1.default.existsSync(detailedInfoFile)) {
return undefined;
}
const content = fs_1.default.readFileSync(detailedInfoFile).toString();
const base = JSON.parse(content);
const out = new Map();
for (const [key, values] of base) {
out.set(key, values.map(v => ({
id: v.id,
name: v.name,
capabilities: new Set(v.capabilities),
context: new Set(v.context)
})));
}
return out;
}
const supportedSymbolMap = new Map([
['not', '🔴'],
['partially', '🔶'],
['fully', '🟩']
]);
function getTestDetails(info, capability) {
if (!info.info) {
return '';
}
const totalTests = info.info.get(capability.id);
const uniqueTests = totalTests?.filter((v, i, a) => a.findIndex(t => t.id === v.id) === i);
if (!uniqueTests || uniqueTests.length === 0) {
return '';
}
const grouped = new Map();
for (const { context } of uniqueTests) {
for (const c of context) {
grouped.set(c, (grouped.get(c) ?? 0) + 1);
}
}
if (grouped.get('desugar-tree-sitter') !== undefined && grouped.get('desugar-tree-sitter') === grouped.get('desugar-shell')) {
grouped.set('desugar', grouped.get('desugar-tree-sitter') ?? 0);
grouped.delete('desugar-shell');
grouped.delete('desugar-tree-sitter');
}
grouped.delete('other'); // opinionated view on the categories
const output = grouped.get('output');
grouped.delete('output');
const testString = [`${uniqueTests.length} test${uniqueTests.length !== 1 ? 's' : ''}`];
// sort by count
const sorted = [...grouped.entries()].sort((a, b) => b[1] - a[1]);
for (const [context, count] of sorted) {
testString.push(`${context}: ${count}`);
}
if (output) {
testString.push(`and backed with output: ${output}`);
}
return ` (${testString.join(', ')})`;
}
function escapeId(id) {
return id.replace(/[^a-zA-Z0-9]/g, '_');
}
async function printSingleCapability(info, depth, index, capability) {
const indent = ' '.repeat(depth);
const indexStr = index.toString().padStart(2, ' ');
const nextLineIndent = ' '.repeat(depth + indexStr.length);
const mainLine = `${indent}${indexStr}. <a id='${capability.id}'></a>**${capability.name}** <a href="#${escapeId(capability.id)}">🔗</a>${getTestDetails(info, capability)}`;
let nextLine = '';
if (capability.supported) {
nextLine += `${supportedSymbolMap.get(capability.supported)} `;
}
if (capability.description) {
nextLine += capability.description;
}
if (capability.url) {
nextLine += '\\\nSee ' + (0, strings_1.joinWithLast)(capability.url.map(({ name, href }) => `[${name}](${href})`)) + ' for more info.';
}
nextLine += ' (internal ID: `' + capability.id + '`)';
if (capability.example) {
nextLine += `\n${(0, doc_general_1.prefixLines)(typeof capability.example === 'string' ? capability.example : await capability.example(info.parser), nextLineIndent + '> ')}`;
}
return nextLine ? `${mainLine}\\\n${nextLineIndent}${nextLine}` : mainLine;
}
function summarizeChildren(capabilities) {
const summary = { total: 0, fully: 0, partially: 0, not: 0 };
for (const capability of capabilities) {
if (capability.capabilities) {
const childSummary = summarizeChildren(capability.capabilities);
summary.fully += childSummary.fully;
summary.partially += childSummary.partially;
summary.not += childSummary.not;
summary.total += childSummary.total;
}
if (capability.supported) {
summary[capability.supported]++;
summary.total++;
}
}
return summary;
}
function printSummary(sum) {
return `${sum.fully} fully, ${sum.partially} partially, ${sum.not} not supported`;
}
async function printAsMarkdown(info, capabilities, depth = 0, lines = []) {
for (let i = 0; i < capabilities.length; i++) {
const capability = capabilities[i];
const result = await printSingleCapability(info, depth, i + 1, capability);
lines.push(result);
if (capability.capabilities) {
const summary = summarizeChildren(capability.capabilities);
lines.push(`\n\n${' '.repeat(depth + 1)}<details open><summary>${summary.total} child${summary.total === 1 ? '' : 'ren'} (${printSummary(summary)})</summary>\n\n`);
await printAsMarkdown(info, capability.capabilities, depth + 1, lines);
lines.push(`\n\n${' '.repeat(depth + 1)}</details>\n\n`);
if (depth === 0) {
lines.push('\n\n' + ' '.repeat(depth + 1) + '-'.repeat(42) + '\n\n');
}
}
}
return lines.join('\n');
}
function getPreamble() {
return `${(0, doc_auto_gen_1.autoGenHeader)({ filename: module.filename, purpose: 'current capabilities' })}
Each capability has an id that can be used to link to it (use the link symbol to get a direct link to the capability).
The internal id is also mentioned in the capability description. This id can be used to reference the capability in a labeled test within flowR.
Besides, we use colored bullets like this:
| <!-- --> | <!-- --> |
| ---------------------- | ----------------------------------------------------- |
| ${supportedSymbolMap.get('fully')} | _flowR_ is capable of handling this feature _fully_ |
| ${supportedSymbolMap.get('partially')} | _flowR_ is capable of handling this feature _partially_ |
| ${supportedSymbolMap.get('not')} | _flowR_ is _not_ capable of handling this feature |
:cloud: This could be a feature diagram... :cloud:
${(0, doc_structure_1.block)({
type: 'NOTE',
content: `
The capabilities are a qualitative measure of the features that flowR can handle.
Statements like "flowR can fully handle 50/80 capabilities" are discouraged as the capabilities may have a vastly different granularity.
Please prefer using a statement like "flowR has only partial support for feature 'XY'" (or simply reference this document) within the flowR sources.
`
})}
`;
}
async function print(parser) {
/* check if the detailed test data is available */
if (!fs_1.default.existsSync(detailedInfoFile)) {
console.warn('\x1b[31mNo detailed test data available. Run the full tests (npm run test-full) to generate it.\x1b[m');
}
return getPreamble() + await printAsMarkdown({ parser, info: obtainDetailedInfos() }, data_1.flowrCapabilities.capabilities);
}
/** if we run this script, we want a Markdown representation of the capabilities */
if (require.main === module) {
(0, log_1.setMinLevelOfAllLogs)(6 /* LogLevel.Fatal */);
void tree_sitter_executor_1.TreeSitterExecutor.initTreeSitter().then(() => {
const parser = new tree_sitter_executor_1.TreeSitterExecutor();
void print(parser).then(str => {
console.log(str);
});
});
}
//# sourceMappingURL=print-capabilities-markdown.js.map