Include the POV field in the file-list hover tooltip

Header parsing now also lifts a `POV:` line (any heading level). The
file-list tooltip combines it with the slug — "POV: <name>" then the
slug synopsis — cached per file alongside the slug (HoverInfo) and read
live from the buffer for the selected file. Adds a POV parse test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kRuP7JvXoUGdNNeg5ZSs
This commit is contained in:
landon
2026-08-14 06:49:53 -05:00
parent 9c4498a680
commit c926b6696f
3 changed files with 84 additions and 34 deletions
+18
View File
@@ -29,6 +29,8 @@ pub struct Header {
pub title: Option<String>,
/// Value of the `# Slug:` line, if present and non-empty.
pub slug: Option<String>,
/// Value of the `POV:` line, if present and non-empty.
pub pov: Option<String>,
/// Word-count target from a `Word Count Target:` line, if present and parsable.
pub goal: Option<WordGoal>,
/// The prose body, with comments, the header block, and metadata lines removed.
@@ -39,6 +41,7 @@ pub struct Header {
enum Meta {
Title,
Slug,
Pov,
WordCount,
}
@@ -105,6 +108,7 @@ fn meta_line(line: &str) -> Option<(Meta, String)> {
for (key, meta) in [
("title:", Meta::Title),
("slug:", Meta::Slug),
("pov:", Meta::Pov),
("word count target:", Meta::WordCount),
("word count goal:", Meta::WordCount),
("word count:", Meta::WordCount),
@@ -134,10 +138,12 @@ pub fn parse(markdown: &str, marker: &str) -> Header {
let mut title = None;
let mut slug = None;
let mut pov = None;
let mut goal = None;
let mut set = |meta: Meta, value: String| match meta {
Meta::Title => title = Some(value),
Meta::Slug => slug = Some(value),
Meta::Pov => pov = Some(value),
Meta::WordCount => goal = parse_goal(&value),
};
@@ -173,6 +179,7 @@ pub fn parse(markdown: &str, marker: &str) -> Header {
Header {
title: title.filter(|s| !s.is_empty()),
slug: slug.filter(|s| !s.is_empty()),
pov: pov.filter(|s| !s.is_empty()),
goal,
body: body_lines.join("\n").trim().to_string(),
}
@@ -265,4 +272,15 @@ mod tests {
let md = "# Title: Plain\n### Rough Draft:\n\nText.";
assert_eq!(parse(md, MARKER).goal, None);
}
#[test]
fn parses_pov_at_heading_level_two() {
let md = "# Title: Bixby\n## POV: Bixby\n### Rough Draft:\n\nThe prose.";
let h = parse(md, MARKER);
assert_eq!(h.pov.as_deref(), Some("Bixby"));
assert_eq!(h.body, "The prose.");
// A prose line that merely starts with "POV" is not metadata.
let md2 = "### Rough Draft:\n\nPOV of the crowd was tense.";
assert_eq!(parse(md2, MARKER).pov, None);
}
}