UNPKG

@dodona/papyros

Version:

Scratchpad for multiple programming languages in the browser.

197 lines 6.42 kB
import { StateField } from "@codemirror/state"; import { StateEffect } from "@codemirror/state"; import { gutter, GutterMarker } from "@codemirror/view"; import { EditorView } from "@codemirror/view"; import { appendClasses } from "../util/Rendering"; /** * Helper class to create markers in the gutter */ class SimpleMarker extends GutterMarker { constructor( // Function to create the DOM element createMarker) { super(); this.createMarker = createMarker; } toDOM() { return this.createMarker(); } } export class Gutters { constructor(config) { this.config = config; this.effect = StateEffect.define(); this.state = StateField.define({ create: () => { return new Map(); }, update: (current, transaction) => { const updatedMap = new Map(current); for (const e of transaction.effects) { if (e.is(this.effect)) { updatedMap.set(e.value.lineNr, e.value); } } return updatedMap; } }); } applyClasses(marker) { const classes = { classNames: this.config.markerClasses }; appendClasses(classes, "_tw-px-1 papyros-gutter-marker"); marker.elementClass += classes.classNames; return marker; } hasMarker(view, lineNr) { const guttersInfo = view.state.field(this.state); return guttersInfo.has(lineNr) && guttersInfo.get(lineNr).on; } /** * Set a marker with the given info * @param {EditorView} view View in which the Gutters live * @param {Info} info Info used to render the marker */ setMarker(view, info) { if (this.hasMarker(view, info.lineNr) !== info.on) { view.dispatch({ effects: this.effect.of(info) }); } } /** * @param {EditorView} view The view in which the Gutters live * @return {Set<number>} The 1-based line numbers with a breakpoint */ getMarkedLines(view) { const markedLines = new Set(); const guttersInfo = view.state.field(this.state); guttersInfo.forEach((info, lineNr) => { if (info.on) { markedLines.add(lineNr); } }); return markedLines; } /** * @return {Extension} The Gutters as a CodeMirror Extension */ toExtension() { // TODO correct type: https://github.com/codemirror/codemirror.next/issues/839 const handlers = {}; if (this.config.onClick) { handlers["mousedown"] = (view, line) => { const markings = view.state.field(this.state); const lineNr = view.state.doc.lineAt(line.from).number; const markerInfo = markings.get(lineNr); // Line numbers start at 1 this.config.onClick(view, markerInfo); }; } return [ this.state, gutter({ class: `cm-${this.config.name}-gutter`, lineMarker: (view, line) => { // Lookup whether the element should be drawn const guttersInfo = view.state.field(this.state); const lineNr = view.state.doc.lineAt(line.from).number; if (guttersInfo.has(lineNr) && guttersInfo.get(lineNr).on) { return this.applyClasses(this.marker(guttersInfo.get(lineNr))); } else { return null; } }, lineMarkerChange: update => { return update.startState.field(this.state) !== update.state.field(this.state); }, initialSpacer: () => { return this.applyClasses(this.marker({ lineNr: -1, on: true })); }, domEventHandlers: handlers }), this.config.extraExtensions || [] ]; } } /** * Gutters to show and allow toggling of breakpoints */ export class BreakpointsGutter extends Gutters { constructor() { super({ name: "breakpoint", onClick: (view, info) => { info.on = !info.on; this.setMarker(view, info); }, extraExtensions: [ EditorView.baseTheme({ ".cm-breakpoint-gutter .cm-gutterElement": { color: "red", paddingLeft: "5px", cursor: "default" } }) ] }); } marker() { return new SimpleMarker(() => document.createTextNode("🔴")); } } /** * Gutters to show a checkmark for used input */ export class UsedInputGutters extends Gutters { constructor() { super({ name: "input" }); } marker(info) { return new SimpleMarker(() => { const node = document.createElement("div"); node.replaceChildren(document.createTextNode("✔")); node.setAttribute("title", info.title); // Text interface tells us that more complex node will be processed into Text nodes return node; }); } } class DebugMarker extends GutterMarker { toDOM() { const icon = document.createElement("i"); icon.classList.add("mdi", "mdi-arrow-right-bold", "mdi-18"); return icon; } } /** * shows the debugged line */ export class DebugLineGutter extends Gutters { constructor() { super({ name: "debugline", extraExtensions: [ EditorView.baseTheme({ ".cm-debugline-gutter .cm-gutterElement": { lineHeight: "20px", marginRight: "-5px", fontSize: "18px" } }) ] }); this.activeLine = 1; } marker() { return new DebugMarker(); } markLine(view, lineNr) { this.setMarker(view, { lineNr: this.activeLine, on: false }); this.setMarker(view, { lineNr, on: true }); this.activeLine = lineNr; } } //# sourceMappingURL=Gutters.js.map