2025-07-19 14:39:44 +01:00
|
|
|
import * as vscode from 'vscode';
|
2025-07-20 03:36:43 +01:00
|
|
|
import * as path from 'path';
|
|
|
|
import { BranFlakesExecutorVisitor } from '../exec/BranFlakesExecutorVisitor';
|
2025-07-19 14:39:44 +01:00
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
interface BFRunTaskDefinition extends vscode.TaskDefinition {
|
2025-07-19 18:50:14 +01:00
|
|
|
file?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CustomExecutionTaskProvider implements vscode.TaskProvider {
|
2025-07-20 00:02:45 +01:00
|
|
|
static type: string = 'bf-run';
|
2025-07-19 18:50:14 +01:00
|
|
|
tasks: vscode.Task[] | undefined;
|
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
constructor(private workspaceRoot: string | undefined) {
|
2025-07-19 18:50:14 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-07-19 14:39:44 +01:00
|
|
|
provideTasks(token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task[]> {
|
2025-07-19 18:50:14 +01:00
|
|
|
if (this.tasks !== undefined) { return this.tasks; }
|
2025-07-20 03:36:43 +01:00
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
this.tasks = [this.getTaskFromDefinition(undefined)];
|
|
|
|
return this.tasks;
|
|
|
|
}
|
2025-07-19 18:50:14 +01:00
|
|
|
|
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
getTaskFromDefinition(fileName: string | undefined): vscode.Task {
|
|
|
|
const definition: BFRunTaskDefinition = {
|
2025-07-20 00:02:45 +01:00
|
|
|
type: CustomExecutionTaskProvider.type,
|
|
|
|
file: fileName
|
2025-07-19 18:50:14 +01:00
|
|
|
};
|
2025-07-20 03:36:43 +01:00
|
|
|
return new vscode.Task(definition, vscode.TaskScope.Workspace, `bf: run: current file`, CustomExecutionTaskProvider.type,
|
|
|
|
new vscode.CustomExecution(async () => {
|
2025-07-20 00:02:45 +01:00
|
|
|
return new CustomBuildTaskTerminal(definition.file);
|
2025-07-19 18:50:14 +01:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolveTask(_task: vscode.Task, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task> {
|
2025-07-20 03:36:43 +01:00
|
|
|
const definition: BFRunTaskDefinition = <any>_task.definition;
|
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
const fileNameRecovered = definition.file;
|
2025-07-20 03:36:43 +01:00
|
|
|
const taskName = `bf: run: ` + (fileNameRecovered ?? 'current file');
|
|
|
|
return new vscode.Task(definition, vscode.TaskScope.Workspace, taskName, CustomExecutionTaskProvider.type,
|
|
|
|
new vscode.CustomExecution(async () => {
|
2025-07-20 00:02:45 +01:00
|
|
|
return new CustomBuildTaskTerminal(definition.file);
|
|
|
|
})
|
|
|
|
);
|
2025-07-19 14:39:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
function replaceLFWithCRLF(data: string) {
|
|
|
|
return data.replace(/(?<!\r)\n/gm, '\r\n');
|
|
|
|
}
|
|
|
|
|
2025-07-19 14:39:44 +01:00
|
|
|
|
|
|
|
class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
|
|
|
|
private writeEmitter = new vscode.EventEmitter<string>();
|
|
|
|
private closeEmitter = new vscode.EventEmitter<number>();
|
2025-07-19 18:50:14 +01:00
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
private readEmitter = new vscode.EventEmitter<void>();
|
|
|
|
inputQueue: number[] = [];
|
|
|
|
|
|
|
|
private openDocument: vscode.TextDocument | undefined;
|
2025-07-19 18:50:14 +01:00
|
|
|
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
|
2025-07-19 14:39:44 +01:00
|
|
|
onDidClose?: vscode.Event<number> = this.closeEmitter.event;
|
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
handleInput(data: string): void {
|
2025-07-20 03:36:43 +01:00
|
|
|
// this.writeEmitter.fire(`Echo(${data.length})` + data);
|
|
|
|
const newData = [...data].map(e => e.charCodeAt(0));
|
|
|
|
console.log('new input', newData);
|
|
|
|
this.inputQueue.push(...newData);
|
|
|
|
this.readEmitter.fire();
|
2025-07-20 00:02:45 +01:00
|
|
|
}
|
2025-07-19 14:39:44 +01:00
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
constructor(private fileName?: string) {
|
2025-07-19 14:39:44 +01:00
|
|
|
}
|
|
|
|
|
2025-07-20 03:36:43 +01:00
|
|
|
open(_initialDimensions: vscode.TerminalDimensions | undefined): void {
|
2025-07-19 14:39:44 +01:00
|
|
|
// At this point we can start using the terminal.
|
2025-07-20 03:36:43 +01:00
|
|
|
this.openDocumentForTask().then(this.doExecution.bind(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
getPath(fileLocationString: string | undefined, fileName: string) {
|
|
|
|
if (fileLocationString === undefined) { return vscode.Uri.file(fileName); }
|
|
|
|
return vscode.Uri.file(path.resolve(fileLocationString, fileName));
|
|
|
|
}
|
|
|
|
|
|
|
|
private async openDocumentForTask() {
|
|
|
|
let openDocument: vscode.TextDocument;
|
|
|
|
if (this.fileName !== undefined) {
|
|
|
|
try {
|
|
|
|
const fileLocationPathString = vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath;
|
|
|
|
const finalPath = this.getPath(fileLocationPathString, this.fileName);
|
|
|
|
openDocument = await vscode.workspace.openTextDocument(finalPath);
|
|
|
|
} catch (e) {
|
|
|
|
vscode.window.showErrorMessage('Failed to open file ' + this.fileName);
|
|
|
|
this.closeEmitter.fire(2);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
openDocument = vscode.window.activeTextEditor.document;
|
|
|
|
}
|
2025-07-20 00:02:45 +01:00
|
|
|
this.openDocument = openDocument;
|
2025-07-19 14:39:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
close(): void {
|
|
|
|
// The terminal has been closed. Shutdown the build.
|
|
|
|
}
|
|
|
|
|
2025-07-20 00:02:45 +01:00
|
|
|
private async doExecution(): Promise<void> {
|
2025-07-20 03:36:43 +01:00
|
|
|
this.writeEmitter.fire('[bf] Requested execution of ' + this.fileName + '\r\n');
|
|
|
|
const cus = this;
|
|
|
|
try {
|
|
|
|
|
|
|
|
await BranFlakesExecutorVisitor.run(this.openDocument.getText(),
|
|
|
|
this.openDocument.uri.fsPath,
|
|
|
|
{
|
|
|
|
getInput() {
|
|
|
|
return new Promise((res, rej) => {
|
|
|
|
if (cus.inputQueue.length > 0) {
|
|
|
|
const char = cus.inputQueue.shift();
|
|
|
|
console.log('consumed input', char);
|
|
|
|
res(char);
|
|
|
|
} else {
|
|
|
|
const dispose: vscode.Disposable[] = [];
|
|
|
|
cus.readEmitter.event(e => {
|
|
|
|
const char = cus.inputQueue.shift();
|
|
|
|
console.log('consumed input async', char);
|
|
|
|
//clear the earliest disposable
|
|
|
|
dispose.shift()?.dispose();
|
|
|
|
res(char);
|
|
|
|
}, null, dispose);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
async (data) => {
|
|
|
|
// console.log('output', data);
|
|
|
|
this.writeEmitter.fire(replaceLFWithCRLF(data));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
this.closeEmitter.fire(0);
|
|
|
|
} catch (e) {
|
|
|
|
this.closeEmitter.fire(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-07-19 14:39:44 +01:00
|
|
|
}
|
|
|
|
}
|