From 90659c9c7ef65e84fd0e25c0b84309a573de28a1 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 21 Dec 2023 19:04:54 +0100 Subject: [PATCH] Make frontmatter foldable --- common/markdown_parser/parser.ts | 5 +++++ web/cm_plugins/block.ts | 38 ++++++++++++++++++++++---------- web/cm_plugins/util.ts | 32 --------------------------- 3 files changed, 31 insertions(+), 44 deletions(-) diff --git a/common/markdown_parser/parser.ts b/common/markdown_parser/parser.ts index ab32b716..3d9d22d2 100644 --- a/common/markdown_parser/parser.ts +++ b/common/markdown_parser/parser.ts @@ -468,6 +468,11 @@ export default function buildMarkdown(mdExtensions: MDExt[]): Language { from: state.doc.lineAt(tree.from).to, to: tree.to, }), + // Fold frontmatter + FrontMatter: (tree) => ({ + from: tree.from, + to: tree.to, + }), }), styleTags({ diff --git a/web/cm_plugins/block.ts b/web/cm_plugins/block.ts index 367395c4..64118128 100644 --- a/web/cm_plugins/block.ts +++ b/web/cm_plugins/block.ts @@ -1,4 +1,4 @@ -import { Decoration, EditorState, syntaxTree } from "../deps.ts"; +import { Decoration, EditorState, foldedRanges, syntaxTree } from "../deps.ts"; import { decoratorStateField, HtmlWidget, @@ -8,6 +8,7 @@ import { function hideNodes(state: EditorState) { const widgets: any[] = []; + const foldRanges = foldedRanges(state); syntaxTree(state).iterate({ enter(node) { if ( @@ -33,23 +34,36 @@ function hideNodes(state: EditorState) { node.name === "FrontMatterMarker" ) { const parent = node.node.parent!; + + const folded = foldRanges.iter(); + let shouldShowFrontmatterBanner = false; + while (folded.value) { + // Check if cursor is in the folded range + if (isCursorInRange(state, [folded.from, folded.to])) { + // console.log("Cursor is in folded area, "); + shouldShowFrontmatterBanner = true; + break; + } + folded.next(); + } if (!isCursorInRange(state, [parent.from, parent.to])) { widgets.push( Decoration.line({ class: "sb-line-frontmatter-outside", }).range(node.from), ); - if (parent.from === node.from) { - // Only put this on the first line of the frontmatter - widgets.push( - Decoration.widget({ - widget: new HtmlWidget( - `frontmatter`, - "sb-frontmatter-marker", - ), - }).range(node.from), - ); - } + shouldShowFrontmatterBanner = true; + } + if (shouldShowFrontmatterBanner && parent.from === node.from) { + // Only put this on the first line of the frontmatter + widgets.push( + Decoration.widget({ + widget: new HtmlWidget( + `frontmatter`, + "sb-frontmatter-marker", + ), + }).range(node.from), + ); } } }, diff --git a/web/cm_plugins/util.ts b/web/cm_plugins/util.ts index 37c6d548..65c896f1 100644 --- a/web/cm_plugins/util.ts +++ b/web/cm_plugins/util.ts @@ -6,7 +6,6 @@ import { DecorationSet, EditorState, EditorView, - foldedRanges, StateField, Transaction, WidgetType, @@ -146,34 +145,3 @@ export function isCursorInRange(state: EditorState, range: [number, number]) { * Decoration to simply hide anything. */ export const invisibleDecoration = Decoration.replace({}); - -/** - * Returns the lines of the editor that are in the given range and not folded. - * This function is of use when you need to get the lines of a particular - * block node and add line decorations to each line of it. - * - * @param view - Editor view - * @param from - Start of the range - * @param to - End of the range - * @returns A list of line blocks that are in the range - */ -export function editorLines(view: EditorView, from: number, to: number) { - let lines = view.viewportLineBlocks.filter((block) => - // Keep lines that are in the range - checkRangeOverlap([block.from, block.to], [from, to]) - ); - - const folded = foldedRanges(view.state).iter(); - while (folded.value) { - lines = lines.filter( - (line) => - !checkRangeOverlap( - [folded.from, folded.to], - [line.from, line.to], - ), - ); - folded.next(); - } - - return lines; -}