Files
mixly3/boards/default_src/python_skulpt_car/others/python-shell.js

208 lines
6.9 KiB
JavaScript

import MixpyProject from './mixpy-project';
import PyEngine from './py-engine';
import { Workspace, Msg } from 'mixly';
import StatusBarImage from './statusbar-image';
class PythonShell {
static {
this.pythonShell = null;
this.init = async function () {
StatusBarImage.init();
this.pythonShell = new PythonShell();
}
this.steprun = function () {
const mainWorkspace = Workspace.getMain();
const editor = mainWorkspace.getEditorsManager().getActive();
const code = editor.getCode();
return this.pythonShell.steprun(code);
}
this.run = function () {
const mainWorkspace = Workspace.getMain();
const editor = mainWorkspace.getEditorsManager().getActive();
const code = editor.getCode();
return this.pythonShell.run(code);
}
this.stop = function () {
return this.pythonShell.stop();
}
}
#statusBarTerminal_ = null;
#statusBarImage_ = null;
#statusBarsManager_ = null;
#cursor_ = {
row: 0,
column: 0
};
#prompt_ = '';
#inputResolve_ = null;
#inputReject_ = null;
#waittingForInput_ = false;
#running_ = false;
#pyEngine_ = null;
#onCursorChangeEvent_ = () => this.#onCursorChange_();
#commands_ = [
{
name: 'REPL-Enter',
bindKey: 'Enter',
exec: (editor) => {
const session = editor.getSession();
const cursor = session.selection.getCursor();
if (cursor.row === this.#cursor_.row) {
const newPos = this.#statusBarTerminal_.getEndPos();
let str = this.#statusBarTerminal_.getValueRange(this.#cursor_, newPos);
str = str.replace(this.#prompt_, '');
this.#inputResolve_?.(str);
this.#inputResolve_ = null;
this.#inputReject_ = null;
this.#statusBarTerminal_.addValue('\n');
this.#exitInput_();
return true;
}
return false;
}
}, {
name: 'REPL-ChangeEditor',
bindKey: 'Delete|Ctrl-X|Backspace',
exec: (editor) => {
const session = editor.getSession();
const cursor = session.selection.getCursor();
if (cursor.row < this.#cursor_.row || cursor.column <= this.#cursor_.column) {
return true;
}
return false;
}
}
];
constructor() {
const mainWorkspace = Workspace.getMain();
this.#statusBarsManager_ = mainWorkspace.getStatusBarsManager();
this.#statusBarTerminal_ = this.#statusBarsManager_.getStatusBarById('output');
this.#statusBarImage_ = this.#statusBarsManager_.getStatusBarById('images');
this.#pyEngine_ = new PyEngine({}, new MixpyProject());
this.#pyEngine_.loadEngine(this.#statusBarImage_.getContent().children()[0]);
this.#addEventsListener_();
}
#addEventsListener_() {
const events = this.#pyEngine_.getEvents();
events.bind('finished', () => {
this.#running_ = false;
this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}==`);
});
events.bind('output', (data) => {
this.#statusBarTerminal_.addValue(data.content);
});
events.bind('error', (data) => {
this.#running_ = false;
this.#statusBarTerminal_.addValue(`\n${data.toString()}\n`);
});
events.bind('input', (data) => {
const prompt = String(data?.content?.prompt);
this.#statusBarTerminal_.addValue(`>>> ${prompt}`);
this.#prompt_ = prompt;
this.#inputResolve_ = data.resolve;
this.#inputReject_ = data.reject;
this.#enterInput_();
});
events.bind('display', (data) => {
this.#statusBarsManager_.changeTo('images');
this.#statusBarImage_.display(data);
});
}
#onCursorChange_() {
const editor = this.#statusBarTerminal_.getEditor();
const session = editor.getSession();
const cursor = session.selection.getCursor();
editor.setReadOnly(
cursor.row < this.#cursor_.row || cursor.column < this.#cursor_.column
);
}
#enterInput_() {
if (!this.#running_) {
return;
}
this.#waittingForInput_ = true;
this.#cursor_ = this.#statusBarTerminal_.getEndPos();
const editor = this.#statusBarTerminal_.getEditor();
editor.setReadOnly(false);
editor.focus();
const session = editor.getSession();
session.selection.on('changeCursor', this.#onCursorChangeEvent_);
editor.commands.addCommands(this.#commands_);
}
#exitInput_() {
this.#waittingForInput_ = false;
const editor = this.#statusBarTerminal_.getEditor();
const session = editor.getSession();
session.selection.off('changeCursor', this.#onCursorChangeEvent_);
editor.commands.removeCommands(this.#commands_);
this.#prompt_ = '';
this.#inputResolve_?.('');
// this.#inputReject_?.({});
this.cursor_ = { row: 0, column: 0 };
editor.setReadOnly(true);
}
addPrompt(prompt, resolve, reject) {
this.#statusBarTerminal_.addValue(prompt);
this.#prompt_ = prompt;
this.#inputResolve_ = resolve;
this.#inputReject_ = reject;
this.#enterInput_();
}
async steprun(code) {
await this.stop();
this.#statusBarsManager_.changeTo('output');
this.#statusBarsManager_.show();
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
this.#running_ = true;
if (code.indexOf('import blocklygame') !== -1
|| code.indexOf('from blocklygame import') !== -1) {
this.#statusBarsManager_.changeTo('images');
}
this.#pyEngine_.steprun(code);
}
async run(code) {
await this.stop();
this.#statusBarsManager_.changeTo('output');
this.#statusBarsManager_.show();
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
this.#running_ = true;
if (code.indexOf('import blocklygame') !== -1
|| code.indexOf('from blocklygame import') !== -1) {
this.#statusBarsManager_.changeTo('images');
}
this.#pyEngine_.run(code);
}
async stop() {
if (this.#waittingForInput_) {
this.#exitInput_();
}
if (this.#running_) {
this.#pyEngine_.kill();
await this.sleep(500);
this.#running_ = false;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export default PythonShell;