Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 9 additions & 142 deletions src/cli/wiki.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import { makeDocContextForTypes } from '../documentation/wiki-mk/doc-context';
import { RShell } from '../r-bridge/shell';
import { TreeSitterExecutor } from '../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor';
import type { DocMakerArgs, DocMakerLike, DocMakerOutputArgs } from '../documentation/wiki-mk/doc-maker';
import fs from 'fs';
import { setMinLevelOfAllLogs } from '../../test/functionality/_helper/log';
import { LogLevel } from '../util/log';
import type { OptionDefinition } from 'command-line-usage';
import commandLineUsage from 'command-line-usage';
import commandLineArgs from 'command-line-args';
import { flowrVersion } from '../util/version';
import { WikiFaq } from '../documentation/wiki-faq';
import { ansiFormatter, ColorEffect, Colors, FontStyles } from '../util/text/ansi';
import {
DocCapabilities,
WikiCore, WikiDataflowGraph,
Expand All @@ -26,10 +15,12 @@ import { WikiAnalyzer } from '../documentation/wiki-analyzer';
import { IssueLintingRule } from '../documentation/issue-linting-rule';
import { DocReadme } from '../documentation/doc-readme';
import { WikiLinter } from '../documentation/wiki-linter';
import os from 'os';
import { WikiSetup } from '../documentation/wiki-setup';
import { WikiOverview } from '../documentation/wiki-overview';
import { WikiAbsint } from '../documentation/wiki-absint';
import type { DocMakerLike } from '../documentation/wiki-mk/doc-maker';
import { FlowrRefs } from '../documentation/doc-util/doc-files';
import { wikiCli } from '../documentation/wiki-mk/cli';

export const AllWikiDocuments = [
new WikiFaq(),
Expand All @@ -53,137 +44,13 @@ export const AllWikiDocuments = [
new DocCapabilities()
] as const satisfies DocMakerLike[];

export type ValidWikiDocumentTargets = ReturnType<typeof AllWikiDocuments[number]['getTarget']>;
export type ValidWikiDocumentTargetsNoSuffix = ValidWikiDocumentTargets extends `${infer Name}.${string}` ? Name : never;

function sortByLeastRecentChanged(wikis: DocMakerLike[]): DocMakerLike[] {
return wikis.slice().sort((a, b) => {
const aStat = fs.existsSync(a.getProducer()) ? fs.statSync(a.getProducer()) : undefined;
const bStat = fs.existsSync(b.getProducer()) ? fs.statSync(b.getProducer()) : undefined;
const aMTime = aStat ? aStat.mtime.getTime() : 0;
const bMTime = bStat ? bStat.mtime.getTime() : 0;
return bMTime - aMTime;
});
}

/**
* Updates and optionally re-creates all flowR wikis.
*/
export async function makeAllWikis(force: boolean, filter: string[] | undefined) {
const setupStart = new Date();
console.log('Setting up wiki generation...');
const shell = new RShell();
console.log(' * R shell initialized');
await TreeSitterExecutor.initTreeSitter();
const treeSitter = new TreeSitterExecutor();
console.log(' * Tree-sitter parser initialized');
const ctx = makeDocContextForTypes(shell);
console.log(' * Wiki context prepared');
if(force) {
console.log(ansiFormatter.format('Forcing wiki regeneration (existing files will be overwritten)', { style: FontStyles.Bold, color: Colors.Yellow, effect: ColorEffect.Foreground }));
}
const info: DocMakerArgs & DocMakerOutputArgs = {
ctx,
shell, treeSitter,
force,
readFileSync(f: fs.PathLike) {
try {
return fs.readFileSync(f);
} catch{
return undefined;
}
},
writeFileSync: fs.writeFileSync
};

console.log(`Setup for wiki generation took ${(new Date().getTime() - setupStart.getTime())}ms`);
const changedWikis = new Set<string>();
try {
const sortedDocs = sortByLeastRecentChanged(AllWikiDocuments);
console.log(`Generating ${sortedDocs.length} wikis/docs, sorted by most recently updated...`);
for(const doc of sortedDocs) {
const type = doc.getTarget().toLowerCase().includes('wiki') ? 'Wiki' : 'Doc';
if(filter && !filter.some(f => doc.getTarget().includes(f))) {
console.log(` * Skipping ${type} (filtered out): ${doc.getTarget()}`);
continue;
}
const now = new Date();
console.log(ansiFormatter.format(` [${doc.getTarget()}] Updating ${type}...`, { style: FontStyles.Bold, color: Colors.Cyan, effect: ColorEffect.Foreground }));
const changed = await doc.make(info);
const text = changed ? `${type} updated` : `${type} identical, no changes made`;
if(changed) {
changedWikis.add(doc.getTarget());
}
const color = changed ? Colors.Green : Colors.White;
console.log(ansiFormatter.format(` [${doc.getTarget()}] ${text}: ${doc.getTarget()} (took ${new Date().getTime() - now.getTime()}ms)`, { color, effect: ColorEffect.Foreground }));
for(const out of doc.getWrittenSubfiles()) {
changedWikis.add(out);
console.log(` - Also updated: ${out}`);
}
}
} catch(error) {
console.error('Error while generating documents:', error);
} finally {
shell.close();
}
console.log('All wikis processed in ' + (new Date().getTime() - setupStart.getTime()) + 'ms');
console.log(` * Changed ${changedWikis.size} wiki/doc files.`);
// write a temp file in the os temp dir with the changed wikis
const filename=`${os.tmpdir()}/flowr-wiki-changed-files.txt`;
fs.writeFileSync(`${filename}`, Array.from(changedWikis).join('\n'));
console.log(` * List of changed wikis/docs written to ${filename}`);
}
export type AllWikiDocuments = typeof AllWikiDocuments;

if(require.main === module) {
const wikiOptions: OptionDefinition[] = [
{ name: 'force', alias: 'F', type: Boolean, description: 'Overwrite existing wiki files, even if nothing changes' },
{ name: 'filter', alias: 'f', type: String, multiple: true, description: 'Only generate wikis whose target path contains the given string' },
{ name: 'help', alias: 'h', type: Boolean, description: 'Print this usage guide for the wiki generator' },
{ name: 'keep-alive', type: Boolean, description: 'Keep-alive wiki generator (only sensible with a reloading script like node --watch)' },
];

interface WikiCliOptions {
force: boolean;
filter?: string[];
help: boolean;
'keep-alive': boolean;
}

const optionHelp = [
{
header: `flowR (version ${flowrVersion().toString()})`,
content: 'Documentation (wiki, issue, ...) generator for flowR'
},
{
header: 'Synopsis',
content: [
'$ wiki {bold --help}',
'$ wiki {bold --force}',
'$ wiki {bold --filter} {italic "dataflow"}'
]
},
{
header: 'Options',
optionList: wikiOptions
}
];

setMinLevelOfAllLogs(LogLevel.Fatal);
// parse args
const options = commandLineArgs(wikiOptions) as WikiCliOptions;
if(options.help) {
console.log(commandLineUsage(optionHelp));
process.exit(0);
}
void makeAllWikis(options.force, options.filter).catch(err => {
console.error('Error while generating wikis:', err);
process.exit(1);
}).then(() => {
if(options['keep-alive']) {
console.log('Wiki generator running in keep-alive mode...');
setInterval(() => {
// do nothing, just keep alive
}, 100);
}
wikiCli({
docs: AllWikiDocuments,
refs: FlowrRefs,
header: `flowR (version ${flowrVersion().toString()})`,
content: 'Documentation (wiki, issue, ...) generator for flowR'
});
}
11 changes: 6 additions & 5 deletions src/documentation/data/interface/doc-writing-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { requestFromInput } from '../../../r-bridge/retriever';
import { block, details } from '../../doc-util/doc-structure';
import { TreeSitterExecutor } from '../../../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor';
import { FlowrAnalyzerBuilder } from '../../../project/flowr-analyzer-builder';
import type { AllWikiDocuments } from '../../../cli/wiki';

async function staticSliceExample() {
const analyzer = await new FlowrAnalyzerBuilder()
Expand All @@ -28,7 +29,7 @@ async function staticSliceExample() {
*/
export function explainWritingCode(_shell: RShell, ctx: GeneralDocContext): string {
return `_flowR_ can be used as a ${ctx.linkPage('flowr:npm', 'module')} and offers several main classes and interfaces that are interesting for extension writers
(see the ${ctx.linkPage('flowr:vscode', 'Visual Studio Code extension')} or the ${ctx.linkPage('wiki/Core')} wiki page for more information).
(see the ${ctx.linkPage('flowr:vscode', 'Visual Studio Code extension')} or the ${ctx.linkPage<AllWikiDocuments>('wiki/Core')} wiki page for more information).

### Creating Analyses with _flowR_

Expand All @@ -39,13 +40,13 @@ ${
ctx.code(staticSliceExample, { dropLinesEnd: 2, dropLinesStart: 1, hideDefinedAt: true })
}

For more information, please have a look at the ${ctx.linkPage('wiki/Analyzer')} wiki page, which explains how to construct and use the ${ctx.link(FlowrAnalyzer)} in more detail.
To work with specific perspectives, you can also consult the respective pages like the ${ctx.linkPage('wiki/Dataflow Graph')} or the ${ctx.linkPage('wiki/Abstract Interpretation')} wiki pages.
For more information, please have a look at the ${ctx.linkPage<AllWikiDocuments>('wiki/Analyzer')} wiki page, which explains how to construct and use the ${ctx.link(FlowrAnalyzer)} in more detail.
To work with specific perspectives, you can also consult the respective pages like the ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph')} or the ${ctx.linkPage<AllWikiDocuments>('wiki/Abstract Interpretation')} wiki pages.

### The Pipeline Executor (Low-Level Interface)

Once, in the beginning, _flowR_ was meant to produce a dataflow graph merely to provide *program slices*.
However, with continuous updates, the ${ctx.linkPage('wiki/Dataflow Graph')} repeatedly proves to be the more interesting part.
However, with continuous updates, the ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph')} repeatedly proves to be the more interesting part.
With this, we restructured _flowR_'s originally *hardcoded* pipeline to be far more flexible.
Now, it can be theoretically extended or replaced with arbitrary steps, optional steps, and what we call 'decorations' of these steps.
In short, a slicing pipeline using the ${ctx.link(PipelineExecutor)} looks like this:
Expand Down Expand Up @@ -82,7 +83,7 @@ See the in-code documentation for more information.
### Using the ${ctx.link(RShell)} to Interact with R

The ${ctx.link(RShell)} class allows interfacing with the \`R\`&nbsp;ecosystem installed on the host system.
Please have a look at ${ctx.linkPage('wiki/Engines', 'flowR\'s Engines')} for more information on alternatives (for example, the ${ctx.link(TreeSitterExecutor)}).
Please have a look at ${ctx.linkPage<AllWikiDocuments>('wiki/Engines', 'flowR\'s Engines')} for more information on alternatives (for example, the ${ctx.link(TreeSitterExecutor)}).

${
block({
Expand Down
15 changes: 8 additions & 7 deletions src/documentation/doc-readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { NewIssueUrl } from './doc-util/doc-issue';
import { joinWithLast } from '../util/text/strings';
import type { DocMakerArgs } from './wiki-mk/doc-maker';
import { DocMaker } from './wiki-mk/doc-maker';
import type { AllWikiDocuments } from '../cli/wiki';

const PublicationsMain: { header: string, description: string, doi: string, bibtex: string }[] = [
{
Expand Down Expand Up @@ -194,7 +195,7 @@ ${await documentReplSession(treeSitter, [{
`), ' ')}

* 📚 **dependency analysis**\\
Given your analysis project, flowR offers a plethora of so-called ${ctx.linkPage('wiki/Query API', 'queries')} to get more information about your code.
Given your analysis project, flowR offers a plethora of so-called ${ctx.linkPage<AllWikiDocuments>('wiki/Query API', 'queries')} to get more information about your code.
An important query is the [dependencies query](${FlowrWikiBaseRef}/Query-API#dependencies-query), which shows you the library your project needs,
the data files it reads, the scripts it sources, and the data it outputs.

Expand All @@ -208,17 +209,17 @@ The following showcases the dependency view of the [Visual Studio Code extension
* 🚀 **fast call-graph, data-, and control-flow graphs**\\
Within just [${'<i>' + textWithTooltip(roundToDecimals(await getLatestDfAnalysisTime('"social-science" Benchmark Suite (tree-sitter)'), 1) + ' ms', 'This measurement is automatically fetched from the latest benchmark!') + '</i>'} (as of ${new Date(await getLastBenchmarkUpdate()).toLocaleDateString('en-US', dateOptions)})](${FlowrSiteBaseRef}/wiki/stats/benchmark),
_flowR_ can analyze the data- and control-flow of the average real-world R&nbsp;script. See the ${ctx.linkPage('flowr:benchmarks', 'benchmarks')} for more information,
and consult the ${ctx.linkPage('wiki/Dataflow Graph', 'wiki pages')} for more details on the ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graphs')} as well as ${ctx.linkPage('wiki/Dataflow Graph', 'call graphs', 'perspectives-cg')}.
and consult the ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph', 'wiki pages')} for more details on the ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph', 'dataflow graphs')} as well as ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph', 'call graphs', 'perspectives-cg')}.

${prefixLines(details('Example: Generating a dataflow graph with flowR', `
You can investigate flowR's analyses using the [REPL](${FlowrWikiBaseRef}/Interface#using-the-repl).
Commands like ${getReplCommand('dataflow*')} allow you to view a ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')} for a given R script.
Commands like ${getReplCommand('dataflow*')} allow you to view a ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph', 'dataflow graph')} for a given R script.

Let's have a look at the following example:

${codeBlock('r', getFileContentFromRoot('test/testfiles/example.R'))}

To get the ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')} for this script, you can use the following command:
To get the ${ctx.linkPage<AllWikiDocuments>('wiki/Dataflow Graph', 'dataflow graph')} for this script, you can use the following command:

${await documentReplSession(treeSitter, [{
command: ':dataflow* test/testfiles/example.R',
Expand All @@ -243,8 +244,8 @@ If you are already using flowR and want to give feedback, please consider fillin

## ⭐ Getting Started

To get started with _flowR_ and its features, please check out the ${ctx.linkPage('wiki/Overview')} wiki page.
The ${ctx.linkPage('wiki/Setup')} wiki page explains how you can download and setup _flowR_ on your system.
To get started with _flowR_ and its features, please check out the ${ctx.linkPage<AllWikiDocuments>('wiki/Overview')} wiki page.
The ${ctx.linkPage<AllWikiDocuments>('wiki/Setup')} wiki page explains how you can download and setup _flowR_ on your system.
With docker&nbsp;🐳️, the following line should be enough (and drop you directly into the read-eval-print loop):

${codeBlock('shell', 'docker run -it --rm eagleoutice/flowr')}
Expand Down Expand Up @@ -281,7 +282,7 @@ ${printPublications()}

## 🚀 Contributing

We welcome every contribution! Please check out the ${ctx.linkPage('wiki/Onboarding', 'developer onboarding')} section in the wiki for all the information you will need.
We welcome every contribution! Please check out the ${ctx.linkPage<AllWikiDocuments>('wiki/Onboarding', 'developer onboarding')} section in the wiki for all the information you will need.

### Contributors

Expand Down
11 changes: 10 additions & 1 deletion src/documentation/doc-util/doc-files.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs';
import fs from 'node:fs';
import type { DocRefs } from '../wiki-mk/doc-context';

export const FlowrGithubGroupName = 'flowr-analysis';
export const FlowrGithubBaseRef = `https://github.com/${FlowrGithubGroupName}`;
Expand All @@ -13,6 +14,14 @@ export const FlowrVsCode = 'https://marketplace.visualstudio.com/items?itemName=
export const FlowrPositron = 'https://open-vsx.org/extension/code-inspect/vscode-flowr';
export const FlowrRStudioAddin = `${FlowrGithubBaseRef}/rstudio-addin-flowr`;
export const FlowrRAdapter = `${FlowrGithubBaseRef}/flowr-r-adapter`;

export const FlowrRefs: DocRefs = {
GitHub: {
Ref: FlowrGithubRef,
FileBaseRef: RemoteFlowrFilePathBaseRef
}
};

/**
* Returns a markdown link to the given file path relative to the project root.
*/
Expand Down
Loading
Loading