Yes, i already did it, i try my best to replicated
import {minimalSetup , EditorView} from "codemirror"
import {markdown} from "@codemirror/lang-markdown"
import {languages} from "@codemirror/language-data"
import {ViewPlugin} from "@codemirror/view";
import { Decoration } from "@codemirror/view";
import { syntaxTree } from "@codemirror/language";
export const visibleNodes = ( view, iterator ) => {
for (const { from, to } of view.visibleRanges)
syntaxTree(view.state).iterate({ ...iterator, from, to });
}
export function decorator(view, _) {
const iterable = [ "Document", "ListItem", "BulletList", "OrderedList" ]
const types = { Blockquote: () => { } }
const marks = {
QuoteMark: (from, to, type) => {
const class_ = ["qt-mk"];
if (type in quoteTypes) class_.push(quoteTypes[type]);
else class_.push(quoteTypes["none"]);
return Decoration.mark({
class: class_.join(" ")
}).range(from, to)
},
BlockquoteLine: (from, selected) => Decoration.line({ class: "bq-line " + (selected ? "sw": "") }).range(from),
quoteLine: (from, to, offset) => {
const width = `calc(100% - ${Math.max(0, offset) + 1.2}ch)`;
return Decoration.mark({
class: "bq-text-line",
attributes: { style: `width: ${width}` }
}).range(from, to)
}
}
const getDecorations = (view, node, startLine, lines) => {
const decorations = [];
const { from, to } = node;
const begin = startLine.number;
const iterator = (start, end) => {
let marksCount = 0;
const stack = [];
let marksEnd = 0;
return {
enter({ name, node, from, to }) {
if (name === "Blockquote") stack.push("none");
if (name === "QuoteMark") {
marksEnd = to;
marksCount++;
}
},
leave({ name, from, to }) {
if (name === "Paragraph") {
if (marksEnd < end) {
decorations.push(marks.quoteLine(marksEnd, end, marksEnd - start));
}
}
}
}
}
for ( let index = begin; index < lines + begin; index++) {
const { from, to } = view.state.doc.line(index);
decorations.push(marks.BlockquoteLine(from, true));
syntaxTree(view.state).iterate({ from, to, ... iterator(from, to) })
}
return decorations
}
const widgets = [];
visibleNodes(view, {
enter: ({ name }) => !(name in iterable),
leave: ({ name, from, to, node}) => {
if (name !== "Blockquote") return;
console.log("hola")
const lines = view.state.sliceDoc(from, to).split("\n");
const startLine = view.state.doc.lineAt(from);
widgets.push(... getDecorations(view, node, startLine, lines.length))
},
});
return Decoration.set(widgets, true)
}
export const PluginFactory = (func, conf, pluginSpec = {}) => {
const decorator = class Decorator {
decorations;
conf;
constructor(view) {
this.conf = conf;
this.decorations = func(view, this.conf);
}
update(update) {
if (
update.docChanged ||
update.viewportChanged ||
update.selectionSet
) this.decorations = func(update.view, this.conf);
}
}
return ViewPlugin.fromClass(decorator, { decorations: (v) => v.decorations, ...pluginSpec })
}
export const coreTheme = () => {
const width = "4px";
return EditorView.baseTheme({
"& .bq-text-line": {
lineHeight: "1lh",
display: "inline-block",
paddingLeft: "6px",
textIndent: "-7px",
ariaHidden: "true"
},
})
}
let view = new EditorView({
doc: "some lines before\n> This is an example were the lime wraps, for that it need so much text, and more, and more and more.",
extensions: [
minimalSetup,
EditorView.lineWrapping,
PluginFactory(decorator, {}),
coreTheme(),
markdown({codeLanguages: languages})
],
parent: document.body
})
and it works exactly like the other:
.
Thanks for your time btw