UNPKG

@dodona/papyros

Version:

Scratchpad for multiple programming languages in the browser.

164 lines 6.71 kB
import { Decoration, EditorView, gutter, gutterLineClass, GutterMarker, ViewPlugin, WidgetType, } from "@codemirror/view"; import { RangeSet, StateEffect, StateField } from "@codemirror/state"; export function lineEffectExtension(config) { var _a; const setLines = StateEffect.define(); let currentVal = undefined; const stateField = StateField.define({ create: () => undefined, update(value, tr) { for (const effect of tr.effects) { if (effect.is(setLines)) { currentVal = effect.value; return currentVal; } } if (tr.docChanged && currentVal !== undefined) { // only return the lines that still exist return currentVal.filter((line) => line <= tr.newDoc.lines); } return value; }, }); const lineDecoration = Decoration.line({ class: (_a = config.lineClass) !== null && _a !== void 0 ? _a : "cm-activeLine" }); const lineGutterMarker = new (class extends GutterMarker { constructor() { var _a; super(...arguments); this.elementClass = (_a = config.gutterClass) !== null && _a !== void 0 ? _a : "cm-activeLineGutter"; } })(); const gutterHighlighter = gutterLineClass.compute([stateField], (state) => { const lines = state.field(stateField); if (lines === undefined || lines.length === 0) return RangeSet.empty; const markers = []; for (const lineNum of lines) { if (lineNum <= state.doc.lines) { markers.push(lineGutterMarker.range(state.doc.line(lineNum).from)); } } return RangeSet.of(markers); }); const lineDecorationPlugin = ViewPlugin.fromClass(class { constructor(view) { this.decorations = this.getDecorations(view.state); } update(update) { if (update.state.field(stateField) !== update.startState.field(stateField)) { this.decorations = this.getDecorations(update.state); } } getDecorations(state) { const lines = state.field(stateField); if (lines === undefined || lines.length === 0) return Decoration.none; const decorations = []; for (const lineNum of lines) { if (lineNum <= state.doc.lines) { decorations.push(lineDecoration.range(state.doc.line(lineNum).from)); } } return Decoration.set(decorations); } }, { decorations: (v) => v.decorations, }); const extensions = [stateField, gutterHighlighter, lineDecorationPlugin]; if (config.marker !== undefined) { class CustomMarker extends GutterMarker { toDOM() { const element = document.createElement("div"); element.textContent = config.marker; return element; } } const customMarker = new CustomMarker(); const customGutter = gutter({ class: "cm-custom-gutter", markers: () => { return RangeSet.empty; }, lineMarker: (view, line) => { const lines = view.state.field(stateField); if (lines === undefined || lines.length === 0) { return null; } for (const lineNum of lines) { if (lineNum <= view.state.doc.lines && line.from === view.state.doc.line(lineNum).from) { return customMarker; } } return null; }, }); extensions.push(customGutter); extensions.push(EditorView.baseTheme({ ".cm-custom-gutter": { width: "17px", textAlign: "center", }, })); } return [extensions, setLines, stateField]; } export const [usedLineExtension, setUsedLines, usedLineState] = lineEffectExtension({ marker: "✔" }); export const [debugLineExtension, setDebugLines, debugLineState] = lineEffectExtension({ marker: "▶" }); export const [testLineExtension, setTestLines, testLineState] = lineEffectExtension({ lineClass: "papyros-test-line", gutterClass: "", }); export function testCodeWidgetExtension(translations, handleEdit, handleRemove) { class TestCodeWidget extends WidgetType { toDOM() { const element = document.createElement("div"); element.classList.add("papyros-test-code-widget"); const span = document.createElement("span"); span.innerText = translations.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 = "🖉"; editButton.addEventListener("click", handleEdit); editButton.title = translations.edit; buttons.appendChild(editButton); const deleteButton = document.createElement("a"); deleteButton.classList.add("papyros-icon-link"); deleteButton.innerHTML = "⨯"; deleteButton.addEventListener("click", handleRemove); deleteButton.title = translations.remove; buttons.appendChild(deleteButton); element.appendChild(buttons); return element; } ignoreEvent() { return false; } } const testCodeDecoration = Decoration.widget({ widget: new TestCodeWidget(), side: -1, block: true }); function getDecorations(state) { const lines = state.field(testLineState); if (!lines || lines.length === 0) return Decoration.none; const minLine = Math.min(...lines); if (minLine > state.doc.lines) return Decoration.none; return Decoration.set([testCodeDecoration.range(state.doc.line(minLine).from)]); } return StateField.define({ create(state) { return getDecorations(state); }, update(deco, tr) { for (const effect of tr.effects) { if (effect.is(setTestLines) || tr.docChanged) return getDecorations(tr.state); } return deco.map(tr.changes); }, provide: (f) => EditorView.decorations.from(f), }); } //# sourceMappingURL=Extensions.js.map