Skip to content
Merged
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
15 changes: 11 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
"category": "flowR",
"icon": "$(gear)"
},
{
"command": "vscode-flowr.lint.run",
"icon": "$(bug)",
"title": "Code Quality Analysis (Linter, Preview)",
"category": "flowR"
},
{
"command": "vscode-flowr.internal.slice.dependency",
"title": "Show Slice of Dependency",
Expand Down Expand Up @@ -532,13 +538,13 @@
"test": "vscode-test",
"generate-changelog": "git log ...$(git tag | tail -n 1) --pretty=format:'- %s' --reverse",
"browser": "vscode-test-web --extensionDevelopmentPath=. .",
"copy-wasm-web": "mkdir -p dist/web && cp node_modules/web-tree-sitter/tree-sitter.wasm dist/web && cp node_modules/@eagleoutice/tree-sitter-r/tree-sitter-r.wasm dist/web",
"copy-wasm-web": "npm run copy-wasm && mkdir -p dist/web && cp node_modules/web-tree-sitter/tree-sitter.wasm dist/web && cp node_modules/@eagleoutice/tree-sitter-r/tree-sitter-r.wasm dist/web",
"compile-web": "npm run copy-wasm-web && webpack",
"package-web": "npm run copy-wasm-web && webpack --mode production --devtool hidden-source-map",
"watch-web": "npm run copy-wasm-web && webpack --watch"
},
"dependencies": {
"@eagleoutice/flowr": "^2.2.15",
"@eagleoutice/flowr": "^2.4.0",
"assert": "^2.1.0",
"browserify-zlib": "^0.2.0",
"constants-browserify": "^1.0.0",
Expand Down
16 changes: 10 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { positionSlicers } from './position-slicer';
import { flowrVersion } from '@eagleoutice/flowr/util/version';
import { registerDependencyView } from './flowr/views/dependency-view';
import type { FlowrConfigOptions } from '@eagleoutice/flowr/config';
import { DropPathsOption, InferWorkingDirectory, VariableResolve , defaultConfigOptions, setConfig } from '@eagleoutice/flowr/config';
import { DropPathsOption, InferWorkingDirectory, VariableResolve , defaultConfigOptions } from '@eagleoutice/flowr/config';
import type { BuiltInDefinitions } from '@eagleoutice/flowr/dataflow/environments/built-in-config';
import { deepMergeObject } from '@eagleoutice/flowr/util/objects';
import { registerLintCommands } from './lint';

export const MINIMUM_R_MAJOR = 3;
export const BEST_R_MAJOR = 4;
Expand All @@ -25,10 +26,11 @@ let flowrSession: FlowrSession | undefined;
export async function activate(context: vscode.ExtensionContext) {
extensionContext = context;
outputChannel = vscode.window.createOutputChannel('flowR');
outputChannel.appendLine(`flowR extension activated (ships with flowR v${flowrVersion().toString()})`);
outputChannel.appendLine(`flowR extension activated (ships with flowR v${flowrVersion().toString()}, web: ${isWeb()})`);

registerDiagramCommands(context, outputChannel);
registerSliceCommands(context, outputChannel);
registerLintCommands(context, outputChannel);

updateFlowrConfig();
vscode.workspace.onDidChangeConfiguration(updateFlowrConfig);
Expand Down Expand Up @@ -61,7 +63,7 @@ export async function activate(context: vscode.ExtensionContext) {

context.subscriptions.push(
vscode.commands.registerCommand('vscode-flowr.feedback', () => {
void vscode.window.showQuickPick(['Report a Bug', 'Provide Feedback'], { placeHolder: 'Report a bug or provide Feedback' }).then((result: string | undefined) => {
void vscode.window.showQuickPick(['Report a Bug', 'Provide Feedback'], { placeHolder: 'Report a bug or provide Feedback' }).then((result: string | undefined) => {
if(result === 'Report a Bug') {
const body = encodeURIComponent(`
<!-- Please describe your issue, suggestion or feature request in more detail below! -->
Expand All @@ -86,7 +88,7 @@ ${JSON.stringify(getConfig(), null, 2)}
const url = 'https://docs.google.com/forms/d/e/1FAIpQLScKFhgnh9LGVU7QzqLvFwZe1oiv_5jNhkIO-G-zND0ppqsMxQ/viewform?pli=1';
vscode.env.openExternal(vscode.Uri.parse(url));
}
});
});
}));

statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
Expand Down Expand Up @@ -214,11 +216,13 @@ export function getWasmRootPath(): string {
}
}

export let VSCodeFlowrConfiguration = defaultConfigOptions;

function updateFlowrConfig() {
const config = getConfig();
const wasmRoot = getWasmRootPath();
// we don't want to *amend* here since updates to our extension config shouldn't add additional entries while keeping old ones (definitions etc.)
setConfig(deepMergeObject<FlowrConfigOptions>(defaultConfigOptions, {
VSCodeFlowrConfiguration = deepMergeObject<FlowrConfigOptions>(defaultConfigOptions, {
ignoreSourceCalls: config.get<boolean>(Settings.IgnoreSourceCalls, false),
solver: {
variables: config.get<VariableResolve>(Settings.SolverVariableHandling, VariableResolve.Alias),
Expand All @@ -244,5 +248,5 @@ function updateFlowrConfig() {
treeSitterWasmPath: `${wasmRoot}/tree-sitter.wasm`,
lax: config.get<boolean>(Settings.TreeSitterLax, true)
}]
}));
});
}
36 changes: 20 additions & 16 deletions src/flowr/internal-session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { BEST_R_MAJOR, MINIMUM_R_MAJOR, getConfig, getWasmRootPath, isVerbose, isWeb, updateStatusBar } from '../extension';
import { BEST_R_MAJOR, MINIMUM_R_MAJOR, VSCodeFlowrConfiguration, getConfig, getWasmRootPath, isVerbose, isWeb, updateStatusBar } from '../extension';
import { Settings } from '../settings';
import { graphToMermaid } from '@eagleoutice/flowr/util/mermaid/dfg';
import type { FlowrSession, SliceReturn } from './utils';
Expand All @@ -19,14 +19,16 @@ import { repl, type FlowrReplOptions } from '@eagleoutice/flowr/cli/repl/core';
import { versionReplString } from '@eagleoutice/flowr/cli/repl/print-version';
import { LogLevel, log } from '@eagleoutice/flowr/util/log';
import type { DataflowGraph } from '@eagleoutice/flowr/dataflow/graph/graph';
import { staticSlicing } from '@eagleoutice/flowr/slicing/static/static-slicer';
import { staticSlice } from '@eagleoutice/flowr/slicing/static/static-slicer';
import type { NormalizedAst } from '@eagleoutice/flowr/r-bridge/lang-4.x/ast/model/processing/decorate';
import type { NodeId } from '@eagleoutice/flowr/r-bridge/lang-4.x/ast/model/processing/node-id';
import type { SourceRange } from '@eagleoutice/flowr/util/range';
import { reconstructToCode } from '@eagleoutice/flowr/reconstruct/reconstruct';
import { doNotAutoSelect } from '@eagleoutice/flowr/reconstruct/auto-select/auto-select-defaults';
import { makeMagicCommentHandler } from '@eagleoutice/flowr/reconstruct/auto-select/magic-comments';
import { extractSimpleCfg } from '@eagleoutice/flowr/control-flow/extract-cfg';
import { getEngineConfig } from '@eagleoutice/flowr/config';
import { SliceDirection } from '@eagleoutice/flowr/core/steps/all/static-slicing/00-slice';

const logLevelToScore = {
Silly: LogLevel.Silly,
Expand Down Expand Up @@ -133,7 +135,6 @@ export class FlowrInternalSession implements FlowrSession {
async initialize() {
this.state = 'loading';
updateStatusBar();

this.outputChannel.appendLine('Starting internal flowR engine');

switch(FlowrInternalSession.getEngineToUse()) {
Expand All @@ -148,7 +149,7 @@ export class FlowrInternalSession implements FlowrSession {
}
this.outputChannel.appendLine(`Using options ${JSON.stringify(options)}`);

this.parser = new RShell(options);
this.parser = new RShell(getEngineConfig(VSCodeFlowrConfiguration, 'r-shell'), options);
this.parser.tryToInjectHomeLibPath();

// wait at most 1 second for the version, since the R shell doesn't let us know if the path
Expand Down Expand Up @@ -186,10 +187,13 @@ export class FlowrInternalSession implements FlowrSession {
case 'tree-sitter': {
if(!FlowrInternalSession.treeSitterInitialized) {
try {
this.outputChannel.appendLine('Initializing tree-sitter... (wasm at: ' + getWasmRootPath() + ')');

const timeout = getConfig().get<number>(Settings.TreeSitterTimeout, 60000);
await Promise.race([TreeSitterExecutor.initTreeSitter(), new Promise<void>((_, reject) => setTimeout(() => reject(new Error(`Timeout (${Settings.TreeSitterTimeout} = ${timeout}ms)`)), timeout))]);

this.outputChannel.appendLine('Initializing tree-sitter... (wasm at: ' + getWasmRootPath() + ', timeout: ' + timeout + 'ms)');

await Promise.race([TreeSitterExecutor.initTreeSitter(
getEngineConfig(VSCodeFlowrConfiguration, 'tree-sitter'),
), new Promise<void>((_, reject) => setTimeout(() => reject(new Error(`Timeout (${Settings.TreeSitterTimeout} = ${timeout}ms)`)), timeout))]);
FlowrInternalSession.treeSitterInitialized = true;
} catch(e) {
this.outputChannel.appendLine('Error in init of tree sitter: ' + (e as Error)?.message);
Expand Down Expand Up @@ -242,10 +246,10 @@ export class FlowrInternalSession implements FlowrSession {
return await this.workingOn(this.parser, async s => {
const result = await createDataflowPipeline(s, {
request: requestFromInput(consolidateNewlines(document.getText()))
}).allRemainingSteps();
}, VSCodeFlowrConfiguration).allRemainingSteps();
return graphToMermaid({ graph: result.dataflow.graph, simplified, includeEnvironments: false }).string;
}, 'dfg');

}

async retrieveAstMermaid(document: vscode.TextDocument): Promise<string> {
Expand All @@ -255,7 +259,7 @@ export class FlowrInternalSession implements FlowrSession {
return await this.workingOn(this.parser, async s => {
const result = await createNormalizePipeline(s, {
request: requestFromInput(consolidateNewlines(document.getText()))
}).allRemainingSteps();
}, VSCodeFlowrConfiguration).allRemainingSteps();
return normalizedAstToMermaid(result.normalize.ast);
}, 'ast');
}
Expand All @@ -267,7 +271,7 @@ export class FlowrInternalSession implements FlowrSession {
return await this.workingOn(this.parser, async s => {
const result = await createNormalizePipeline(s, {
request: requestFromInput(consolidateNewlines(document.getText()))
}).allRemainingSteps();
}, VSCodeFlowrConfiguration).allRemainingSteps();
return cfgToMermaid(extractSimpleCfg(result.normalize), result.normalize);
}, 'cfg');
}
Expand All @@ -283,7 +287,7 @@ export class FlowrInternalSession implements FlowrSession {
const threshold = getConfig().get<number>(Settings.SliceRevisitThreshold, 12);
this.outputChannel.appendLine(`[Slice (Internal)] Re-Slice using existing dataflow Graph and AST (threshold: ${threshold})`);
const now = Date.now();
elements = staticSlicing(info.graph, info.ast, criteria, threshold).result;
elements = staticSlice(info.graph, info.ast, criteria, SliceDirection.Backward, threshold).result;
const sliceTime = Date.now() - now;
sliceElements = makeSliceElements(elements, id => info.ast.idMap.get(id)?.location);
const reconstructNow = Date.now();
Expand All @@ -297,7 +301,7 @@ export class FlowrInternalSession implements FlowrSession {
criterion: criteria,
request: requestFromInput(content),
threshold
});
}, VSCodeFlowrConfiguration);
const result = await slicer.allRemainingSteps();

sliceElements = makeSliceElements(result.slice.result, id => result.normalize.idMap.get(id)?.location);
Expand All @@ -320,12 +324,12 @@ export class FlowrInternalSession implements FlowrSession {
}
const result = await createDataflowPipeline(this.parser, {
request: requestFromInput(consolidateNewlines(document.getText()))
}).allRemainingSteps();
}, VSCodeFlowrConfiguration).allRemainingSteps();
if(result.normalize.hasError && (result.normalize.ast.children as unknown[])?.length === 0) {
return { result: {} as QueryResults<T>, hasError: true, dfg: result.dataflow.graph, ast: result.normalize };
}
return {
result: executeQueries({ ast: result.normalize, dataflow: result.dataflow }, query),
result: executeQueries({ ast: result.normalize, dataflow: result.dataflow, config: VSCodeFlowrConfiguration }, query),
hasError: result.normalize.hasError ?? false,
dfg: result.dataflow.graph,
ast: result.normalize
Expand All @@ -337,7 +341,7 @@ export class FlowrInternalSession implements FlowrSession {
return;
}
(config.output as { stdout: (s: string) => void}).stdout(await versionReplString(this.parser));
await repl({ ...config, parser: this.parser });
await repl(VSCodeFlowrConfiguration, { ...config, parser: this.parser });
}

public static getEngineToUse(): KnownParserName {
Expand Down
63 changes: 63 additions & 0 deletions src/lint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as vscode from 'vscode';
import { getFlowrSession } from './extension';
import type { LintingRuleNames, LintingRuleResult } from '@eagleoutice/flowr/linter/linter-rules';
import { LintingRules } from '@eagleoutice/flowr/linter/linter-rules';
import { LintingPrettyPrintContext } from '@eagleoutice/flowr/linter/linter-format';

export function registerLintCommands(context: vscode.ExtensionContext, output: vscode.OutputChannel) {
const linter = new LinterService(output);
context.subscriptions.push(vscode.commands.registerCommand('vscode-flowr.lint.run', async() => {
await linter.runLinting();
}));
}

class LinterService {
private readonly output: vscode.OutputChannel;
private readonly collection = vscode.languages.createDiagnosticCollection('flowr-lint');

constructor(output: vscode.OutputChannel) {
this.output = output;
}

async runLinting(): Promise<void> {
const activeEditor = vscode.window.activeTextEditor;

if(!activeEditor) {
return;
}

const diagnostics: vscode.Diagnostic[] = [];
this.output.appendLine(`[Lint, Preview] Analyzing document: ${activeEditor.document.fileName}`);
const session = await getFlowrSession();

const lint = await session.retrieveQuery(activeEditor.document, [{ type: 'linter' }]);

for(const [ruleName, findings] of Object.entries(lint.result.linter.results)) {
const rule = LintingRules[ruleName as LintingRuleNames];

this.output.appendLine(`[Lint] Found ${findings.results.length} issues for rule: ${ruleName}`);
this.output.appendLine(`[Lint] ${JSON.stringify(findings)}`);

for(const finding of findings.results) {
const range = new vscode.Range(
finding.range[0] - 1,
finding.range[1] - 1,
finding.range[2] - 1,
finding.range[3]
);
diagnostics.push(
new vscode.Diagnostic(
range,
ruleName + ': ' + rule.prettyPrint[LintingPrettyPrintContext.Full](
finding as LintingRuleResult<LintingRuleNames>,
findings['.meta'] as LintingRuleResult<LintingRuleNames>['.meta']
),
vscode.DiagnosticSeverity.Warning
)
);
}
}

this.collection.set(activeEditor.document.uri, diagnostics);
}
}