silverbullet/web/cm_plugins/block_quote.ts

78 lines
1.8 KiB
TypeScript

// BLOCKQUOTE
import {
Decoration,
DecorationSet,
EditorView,
ViewPlugin,
ViewUpdate,
} from "../deps.ts";
import {
editorLines,
isCursorInRange,
iterateTreeInVisibleRanges,
} from "./util.ts";
const quoteMarkRE = /^(\s*>+)/gm;
/**
* Plugin to add style blockquotes.
*/
class BlockQuotePlugin {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.styleBlockquote(view);
}
update(update: ViewUpdate) {
if (
update.docChanged ||
update.viewportChanged ||
update.selectionSet
) {
this.decorations = this.styleBlockquote(update.view);
}
}
/**
* @param view - The editor view
* @returns The blockquote decorations to add to the editor
*/
private styleBlockquote(view: EditorView): DecorationSet {
const widgets: any[] = [];
iterateTreeInVisibleRanges(view, {
enter: ({ name, from, to }) => {
if (name !== "Blockquote") return;
const lines = editorLines(view, from, to);
lines.forEach((line) => {
const lineDec = Decoration.line({
class: "sb-blockquote",
});
widgets.push(lineDec.range(line.from));
});
if (
lines.every(
(line) => !isCursorInRange(view.state, [line.from, line.to]),
)
) {
const marks = Array.from(
view.state.sliceDoc(from, to).matchAll(quoteMarkRE),
)
.map((x) => from + x.index!)
.map((i) =>
Decoration.line({
class: "sb-blockquote-outside",
}).range(i)
);
widgets.push(...marks);
}
},
});
return Decoration.set(widgets, true);
}
}
export const blockQuotePlugin = ViewPlugin.fromClass(BlockQuotePlugin, {
decorations: (v) => v.decorations,
});