@dodona/papyros
Version:
Scratchpad for multiple programming languages in the browser.
134 lines • 5.22 kB
JavaScript
import { Decoration, WidgetType } from "@codemirror/view";
import readOnlyRangesExtension from "codemirror-readonly-ranges";
import { t } from "../util/Util";
import { LineEffectExtension } from "./LineEffectExtension";
const highlightDecoration = Decoration.line({ class: "papyros-test-code" });
// Widget to manage the test code
class TestCodeWidget extends WidgetType {
constructor(testCodeExtension) {
super();
this.testCodeExtension = testCodeExtension;
}
toDOM() {
const element = document.createElement("div");
element.classList.add("papyros-test-code-widget");
const span = document.createElement("span");
span.innerText = t("Papyros.editor.test_code.description");
element.appendChild(span);
const buttons = document.createElement("div");
buttons.classList.add("papyros-test-code-buttons");
const editButton = document.createElement("a");
editButton.classList.add("papyros-icon-link");
editButton.innerHTML = "<i class=\"mdi mdi-pencil\"></i>";
editButton.addEventListener("click", () => {
console.log("edit test code");
this.testCodeExtension.reset(true);
});
editButton.title = t("Papyros.editor.test_code.edit");
buttons.appendChild(editButton);
const deleteButton = document.createElement("a");
deleteButton.classList.add("papyros-icon-link");
deleteButton.innerHTML = "<i class=\"mdi mdi-close\"></i>";
deleteButton.addEventListener("click", () => {
console.log("remove test code");
this.testCodeExtension.reset();
});
deleteButton.title = t("Papyros.editor.test_code.remove");
buttons.appendChild(deleteButton);
element.appendChild(buttons);
return element;
}
ignoreEvent() {
return false;
}
}
class BottomPaddingWidget extends WidgetType {
toDOM() {
const element = document.createElement("div");
element.classList.add("papyros-bottom-padding-widget");
element.appendChild(document.createElement("div"));
return element;
}
}
const bottomPaddingDecoration = Decoration.widget({ widget: new BottomPaddingWidget(), side: 1, block: true });
export class TestCodeExtension {
constructor(view) {
this.lines = "";
this.allowEdit = true;
this.view = view;
this.widget = Decoration.widget({ widget: new TestCodeWidget(this), block: true, side: 1 });
this.lineEffect = new LineEffectExtension(view);
}
get numberOfTestLines() {
return this.lines.split("\n").length;
}
lineFromEnd(line, state = undefined) {
const currentState = state || this.view.state;
const lineFromEnd = currentState.doc.lines - line;
return currentState.doc.line(lineFromEnd);
}
highlightLines() {
for (let i = 0; i < this.numberOfTestLines; i++) {
this.lineEffect.add([highlightDecoration.range(this.lineFromEnd(i).from)]);
}
}
addWidget() {
this.lineEffect.add([
this.widget.range(this.lineFromEnd(this.numberOfTestLines).to),
bottomPaddingDecoration.range(this.lineFromEnd(0).to)
]);
}
clearAllLineEffects() {
this.lineEffect.clear();
}
getReadOnlyRanges(state) {
if (this.allowEdit) {
return [];
}
return [{
from: this.lineFromEnd(this.numberOfTestLines - 1, state).from,
to: undefined // until last line
}];
}
insertTestCode(code) {
var _a;
// insert up to two new lines to separate the test code from the user code
// but only if they are not already there
const finalNewLineCount = ((_a = this.view.state.doc.toString().match(/\n*$/)) === null || _a === void 0 ? void 0 : _a[0].length) || 0;
const newLinesToInsert = Math.max(0, 2 - finalNewLineCount);
this.view.dispatch({ changes: { from: this.lineFromEnd(0).to, insert: "\n".repeat(newLinesToInsert) } });
this.view.dispatch({ changes: { from: this.lineFromEnd(0).to, insert: code } });
this.lines = code;
}
reset(keepCode = false) {
this.allowEdit = true;
this.clearAllLineEffects();
if (this.lines === "") {
return;
}
if (!keepCode) {
this.view.dispatch({ changes: { from: this.lineFromEnd(this.numberOfTestLines).to, to: this.lineFromEnd(0).to, insert: "" } });
}
this.lines = "";
}
set testCode(code) {
this.reset();
if (code === "") {
return;
}
this.insertTestCode(code);
this.allowEdit = false;
this.highlightLines();
this.addWidget();
}
getNonTestCode() {
if (this.allowEdit) {
return this.view.state.doc.toString();
}
return this.view.state.doc.sliceString(0, this.lineFromEnd(this.numberOfTestLines).to);
}
toExtension() {
return [this.lineEffect.toExtension(), readOnlyRangesExtension(this.getReadOnlyRanges.bind(this))];
}
}
//# sourceMappingURL=TestCodeExtension.js.map