UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

174 lines (170 loc) 7.93 kB
"use strict"; 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