Fixes #164: Rewrote all CM view plugins to statefields

This commit is contained in:
Zef Hemel
2022-12-09 16:12:55 +01:00
parent 453c613ef4
commit c8c4271aeb
16 changed files with 433 additions and 739 deletions
+27 -81
View File
@@ -2,18 +2,12 @@
// Original author: Pranav Karawale
// License: Apache License 2.0.
import {
Decoration,
DecorationSet,
EditorView,
ViewPlugin,
ViewUpdate,
} from "../deps.ts";
import { Decoration, EditorState, syntaxTree } from "../deps.ts";
import {
checkRangeOverlap,
decoratorStateField,
invisibleDecoration,
isCursorInRange,
iterateTreeInVisibleRanges,
} from "./util.ts";
/**
@@ -37,22 +31,16 @@ const markTypes = [
];
/**
* Plugin to hide marks when the they are not in the editor selection.
* Ixora hide marks plugin.
*
* This plugin allows to:
* - Hide marks when they are not in the editor selection.
*/
class HideMarkPlugin {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.compute(view);
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged || update.selectionSet) {
this.decorations = this.compute(update.view);
}
}
compute(view: EditorView): DecorationSet {
export function hideMarksPlugin() {
return decoratorStateField((state: EditorState) => {
const widgets: any[] = [];
let parentRange: [number, number];
iterateTreeInVisibleRanges(view, {
syntaxTree(state).iterate({
enter: ({ type, from, to, node }) => {
if (typesWithMarks.includes(type.name)) {
// There can be a possibility that the current node is a
@@ -64,7 +52,7 @@ class HideMarkPlugin {
) {
return;
} else parentRange = [from, to];
if (isCursorInRange(view.state, [from, to])) return;
if (isCursorInRange(state, [from, to])) return;
const innerTree = node.toTree();
innerTree.iterate({
enter({ type, from: markFrom, to: markTo }) {
@@ -83,78 +71,36 @@ class HideMarkPlugin {
},
});
return Decoration.set(widgets, true);
}
});
}
/**
* Ixora hide marks plugin.
*
* This plugin allows to:
* - Hide marks when they are not in the editor selection.
*/
export const hideMarks = () => [
ViewPlugin.fromClass(HideMarkPlugin, {
decorations: (v) => v.decorations,
}),
];
// HEADINGS
class HideHeaderMarkPlugin {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.hideHeaderMark(view);
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged || update.selectionSet) {
this.decorations = this.hideHeaderMark(update.view);
}
}
/**
* Function to decide if to insert a decoration to hide the header mark
* @param view - Editor view
* @returns The `Decoration`s that hide the header marks
*/
private hideHeaderMark(view: EditorView) {
export function hideHeaderMarkPlugin() {
return decoratorStateField((state) => {
const widgets: any[] = [];
const ranges = view.state.selection.ranges;
iterateTreeInVisibleRanges(view, {
syntaxTree(state).iterate({
enter: ({ type, from, to }) => {
if (!type.name.startsWith("ATXHeading")) {
return;
}
// Get the active line
const line = view.lineBlockAt(from);
// If any cursor overlaps with the heading line, skip
const cursorOverlaps = ranges.some(({ from, to }) =>
checkRangeOverlap([from, to], [line.from, line.to])
);
if (cursorOverlaps && type.name === "HeaderMark") {
const line = state.sliceDoc(from, to);
if (isCursorInRange(state, [from, to])) {
widgets.push(
Decoration.line({ class: "sb-header-inside" }).range(from),
);
return;
} else if (cursorOverlaps) {
return;
}
if (
type.name === "HeaderMark" &&
// Setext heading's horizontal lines are not hidden.
/[#]/.test(view.state.sliceDoc(from, to))
) {
const dec = Decoration.replace({});
widgets.push(dec.range(from, to + 1));
}
widgets.push(
invisibleDecoration.range(
from,
from + line.indexOf(" ") + 1,
),
);
},
});
return Decoration.set(widgets, true);
}
});
}
/**
* Plugin to hide the header mark.
*
* The header mark will not be hidden when:
* - The cursor is on the active line
* - The mark is on a line which is in the current selection
*/
export const hideHeaderMarkPlugin = ViewPlugin.fromClass(HideHeaderMarkPlugin, {
decorations: (v) => v.decorations,
});