Add REPL plugin hooks; Add output, output-mode, stderr attributes (#1106)

* Add before, after REPL hooks

* Re-introduce 'output-mode' attribute for py-repl

* Add plugin execution tests

* Documentation

* Changelog

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: mariana <marianameireles@protonmail.com>
This commit is contained in:
Jeff Glass
2023-03-22 20:19:22 -05:00
committed by GitHub
parent 51d51409d3
commit ef793aecf3
10 changed files with 514 additions and 91 deletions

View File

@@ -7,14 +7,16 @@ import { defaultKeymap } from '@codemirror/commands';
import { oneDarkTheme } from '@codemirror/theme-one-dark';
import { getAttribute, ensureUniqueId, htmlDecode } from '../utils';
import { pyExec, pyDisplay } from '../pyexec';
import { pyExec } from '../pyexec';
import { getLogger } from '../logger';
import { InterpreterClient } from '../interpreter_client';
import type { PyScriptApp } from '../main';
import { Stdio } from '../stdio';
const logger = getLogger('py-repl');
const RUNBUTTON = `<svg style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>`;
export function make_PyRepl(interpreter: InterpreterClient) {
export function make_PyRepl(interpreter: InterpreterClient, app: PyScriptApp) {
/* High level structure of py-repl DOM, and the corresponding JS names.
this <py-repl>
@@ -31,6 +33,8 @@ export function make_PyRepl(interpreter: InterpreterClient) {
shadow: ShadowRoot;
outDiv: HTMLElement;
editor: EditorView;
stdout_manager: Stdio | null;
stderr_manager: Stdio | null;
constructor() {
super();
@@ -152,27 +156,19 @@ export function make_PyRepl(interpreter: InterpreterClient) {
*/
async execute(): Promise<void> {
const pySrc = this.getPySrc();
// determine the output element
const outEl = this.getOutputElement();
if (outEl === undefined) {
// this happens if we specified output="..." but we couldn't
// find the ID. We already displayed an error message inside
// getOutputElement, stop the execution.
return;
}
// clear the old output before executing the new code
outEl.innerHTML = '';
const outEl = this.outDiv;
// execute the python code
app.plugins.beforePyReplExec({ interpreter: interpreter, src: pySrc, outEl: outEl, pyReplTag: this });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const pyResult = (await pyExec(interpreter, pySrc, outEl)).result;
// display the value of the last evaluated expression (REPL-style)
if (pyResult !== undefined) {
pyDisplay(interpreter, pyResult, { target: outEl.id });
}
app.plugins.afterPyReplExec({
interpreter: interpreter,
src: pySrc,
outEl: outEl,
pyReplTag: this,
result: pyResult, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
});
this.autogenerateMaybe();
}
@@ -181,21 +177,6 @@ export function make_PyRepl(interpreter: InterpreterClient) {
return this.editor.state.doc.toString();
}
getOutputElement(): HTMLElement {
const outputID = getAttribute(this, 'output');
if (outputID !== null) {
const el = document.getElementById(outputID);
if (el === null) {
const err = `py-repl ERROR: cannot find the output element #${outputID} in the DOM`;
this.outDiv.innerText = err;
return undefined;
}
return el;
} else {
return this.outDiv;
}
}
// XXX the autogenerate logic is very messy. We should redo it, and it
// should be the default.
autogenerateMaybe(): void {
@@ -206,7 +187,15 @@ export function make_PyRepl(interpreter: InterpreterClient) {
const nextExecId = parseInt(lastExecId) + 1;
const newPyRepl = document.createElement('py-repl');
newPyRepl.setAttribute('root', this.getAttribute('root'));
//Attributes to be copied from old REPL to auto-generated REPL
for (const attribute of ['root', 'output-mode', 'output', 'stderr']) {
const attr = getAttribute(this, attribute);
if (attr) {
newPyRepl.setAttribute(attribute, attr);
}
}
newPyRepl.id = this.getAttribute('root') + '-' + nextExecId.toString();
if (this.hasAttribute('auto-generate')) {
@@ -214,20 +203,6 @@ export function make_PyRepl(interpreter: InterpreterClient) {
this.removeAttribute('auto-generate');
}
const outputMode = getAttribute(this, 'output-mode');
if (outputMode) {
newPyRepl.setAttribute('output-mode', outputMode);
}
const addReplAttribute = (attribute: string) => {
const attr = getAttribute(this, attribute);
if (attr) {
newPyRepl.setAttribute(attribute, attr);
}
};
addReplAttribute('output');
newPyRepl.setAttribute('exec-id', nextExecId.toString());
if (this.parentElement) {
this.parentElement.appendChild(newPyRepl);