From b6c1a2d5ced2a5f9c2b5535d743bf05f30e3afdb Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 1 Jun 2026 08:04:42 +0000 Subject: [PATCH] Fix empty titles in Markdown/Quarto file outline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The outline entries showed up at the right lines and jumped correctly, but their titles were blank. The text-extraction walked the heading node's children and collected non-HeaderMark child text — but in the Lezer Markdown grammar a heading has NO child node for its text; the only children are the HeaderMark nodes. The title text lives in the gaps between marks, so the walk collected nothing. Slice the whole heading's source instead and strip the markers: leading/trailing '#'s for ATX headings and the '==='/'---' underline for Setext headings. Co-Authored-By: Claude Opus 4.8 --- .../languages/markdown/document-outline.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/languages/markdown/document-outline.ts b/services/web/frontend/js/features/source-editor/languages/markdown/document-outline.ts index 7f7a5ea4d8..8c32f667b7 100644 --- a/services/web/frontend/js/features/source-editor/languages/markdown/document-outline.ts +++ b/services/web/frontend/js/features/source-editor/languages/markdown/document-outline.ts @@ -33,21 +33,23 @@ const enterMarkdownNode = ( return } - // Collect heading text, skipping the HeaderMark (the leading # characters) - let title = '' - const cursor = node.node.cursor() - if (cursor.firstChild()) { - do { - if (cursor.name !== 'HeaderMark') { - title += state.sliceDoc(cursor.from, cursor.to) - } - } while (cursor.nextSibling()) - } + // In the Lezer Markdown grammar the heading text is NOT a child node — + // the only children are HeaderMark nodes ('#'s for ATX, the '='/'-' + // underline for Setext). The title text sits in the gaps between marks. + // So we slice the whole heading and strip the marker characters: + // - ATX: leading '#'s and any optional trailing '#'s + // - Setext: the trailing underline line ('===' / '---') + const raw = state.sliceDoc(node.from, node.to) + const title = raw + .replace(/^\s*#+\s*/, '') // ATX: leading ### markers + .replace(/\s*#+\s*$/, '') // ATX: optional closing ### markers + .replace(/\n[=-]+\s*$/, '') // Setext: underline line + .trim() items.push({ line: state.doc.lineAt(node.from).number, toLine: state.doc.lineAt(node.to).number, - title: title.trim(), + title, from: node.from, to: node.to, level,