From 3a1f32c817f0b3f28afb9c87215a7502fe48864c Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 12 Apr 2021 17:56:20 +0300 Subject: feat: add "new-topic" command and "new --topic" option This makes it easier to have "topic pages" in a journal, for collecting entries related to a certain topic. The topic page uses ikiwiki's !inline directive to collect pages linking to it. --- jt.md | 26 ++++++++++++++++++++++++++ src/bin/jt2.rs | 15 +++++++++++---- src/error.rs | 14 +++++++++++--- src/journal.rs | 32 +++++++++++++++++++++++++++++++- src/opt.rs | 12 ++++++++++++ src/template.rs | 17 +++++++++++++++++ subplot/jt.py | 7 +++++++ subplot/jt.yaml | 3 +++ 8 files changed, 118 insertions(+), 8 deletions(-) diff --git a/jt.md b/jt.md index d1f3b14..958de77 100644 --- a/jt.md +++ b/jt.md @@ -247,6 +247,32 @@ and draft 0 in jrnl contains "custom new entry template" This is a custom new entry template. ~~~ +## Use topic pages + +Verify that we can create a new topic page and a new entry referring +to that topic page. + +~~~scenario +given an installed jt + +when I run jt2 --dirname jrnl init default "My test journal" +then command is successful + +when I try to run jt2 --editor=none --dirname=jrnl new --topic foo "Abracadabra" +then command fails +then stderr contains "foo" + +when I run jt2 --editor=none --dirname=jrnl new-topic foo "Things about Foo" +then command is successful +then file jrnl/foo.mdwn contains "Things about Foo" + +when I run jt2 --editor=none --dirname=jrnl new --topic foo "Abracadabra" +then command is successful +and there is one draft in jrnl +and draft 0 in jrnl links to "foo" +~~~ + + # Colophon This document is meant to be processed with the [Subplot][] program to diff --git a/src/bin/jt2.rs b/src/bin/jt2.rs index 25a3a13..677bb35 100644 --- a/src/bin/jt2.rs +++ b/src/bin/jt2.rs @@ -3,7 +3,7 @@ use jt2::error::JournalError; use jt2::journal::Journal; use jt2::opt::{Opt, SubCommand}; -use std::path::Path; +use std::path::{Path, PathBuf}; use structopt::StructOpt; fn main() -> anyhow::Result<()> { @@ -17,7 +17,8 @@ fn main() -> anyhow::Result<()> { description, } => init(&config.dirname, &journalname, &description, &config)?, SubCommand::IsJournal => is_journal(&config)?, - SubCommand::New { title } => new_draft(&title, &config)?, + SubCommand::New { title, topic } => new_draft(&title, &topic, &config)?, + SubCommand::NewTopic { path, title } => new_topic(&path, &title, &config)?, SubCommand::Edit { draft } => edit_draft(&draft, &config)?, SubCommand::Finish { draft, basename } => finish_draft(&draft, &basename, &config)?, } @@ -41,9 +42,15 @@ fn is_journal(config: &Configuration) -> anyhow::Result<()> { Ok(()) } -fn new_draft(title: &str, config: &Configuration) -> anyhow::Result<()> { +fn new_draft(title: &str, topic: &Option, config: &Configuration) -> anyhow::Result<()> { let journal = Journal::new(&config.dirname, &config.entries)?; - journal.new_draft(title, &config.editor)?; + journal.new_draft(title, topic, &config.editor)?; + Ok(()) +} + +fn new_topic(path: &Path, title: &str, config: &Configuration) -> anyhow::Result<()> { + let journal = Journal::new(&config.dirname, &config.entries)?; + journal.new_topic(path, title, &config.editor)?; Ok(()) } diff --git a/src/error.rs b/src/error.rs index a6f239e..89a10c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,14 +34,22 @@ pub enum JournalError { #[error("failed to create journal entry {0}: {1}")] WriteEntry(PathBuf, #[source] std::io::Error), - /// To many drafts. - #[error("there are already {0} drafts in {1}, can't create more")] - TooManyDrafts(usize, PathBuf), + /// Failed to write topic page. + #[error("failed to create topic page {0}: {1}")] + WriteTopic(PathBuf, #[source] std::io::Error), /// User chose a draft that doesn't exist. #[error("No draft {0} in {1}")] NoSuchDraft(String, PathBuf), + /// Too many drafts. + #[error("there are already {0} drafts in {1}, can't create more")] + TooManyDrafts(usize, PathBuf), + + /// User chose a topic that doesn't exist. + #[error("No topic page {0}")] + NoSuchTopic(PathBuf), + /// Failed to read draft. #[error("failed to drafts {0}: {1}")] ReadDraft(PathBuf, #[source] std::io::Error), diff --git a/src/journal.rs b/src/journal.rs index 788a34b..41e6d3d 100644 --- a/src/journal.rs +++ b/src/journal.rs @@ -57,7 +57,12 @@ impl Journal { self.entries.clone() } - pub fn new_draft(&self, title: &str, editor: &str) -> Result<(), JournalError> { + pub fn new_draft( + &self, + title: &str, + topic: &Option, + editor: &str, + ) -> Result<(), JournalError> { let drafts = self.drafts(); if !drafts.exists() { std::fs::create_dir(&drafts) @@ -67,6 +72,13 @@ impl Journal { let mut context = Context::new(); context.insert("title", title); context.insert("date", ¤t_timestamp()); + if let Some(ref topic) = topic { + let pathname = topic_path(self.dirname(), topic); + if !pathname.exists() { + return Err(JournalError::NoSuchTopic(topic.to_path_buf())); + } + context.insert("topic", &topic.display().to_string()); + } let pathname = self.pick_file_id(&drafts)?; let text = self.templates.new_draft(&context)?; @@ -143,6 +155,18 @@ impl Journal { })?; Ok(()) } + + pub fn new_topic(&self, path: &Path, title: &str, editor: &str) -> Result<(), JournalError> { + let mut context = Context::new(); + context.insert("title", title); + + let pathname = topic_path(self.dirname(), path); + let text = self.templates.new_topic(&context)?; + std::fs::write(&pathname, text) + .map_err(|err| JournalError::WriteTopic(pathname.to_path_buf(), err))?; + self.edit(editor, &pathname)?; + Ok(()) + } } fn is_dir(path: &Path) -> bool { @@ -153,6 +177,12 @@ fn is_dir(path: &Path) -> bool { } } +fn topic_path(dirname: &Path, topic: &Path) -> PathBuf { + let mut path = dirname.join(topic); + path.set_extension("mdwn"); + path +} + fn current_timestamp() -> String { let now = Local::now(); let now: DateTime = DateTime::from(now); diff --git a/src/opt.rs b/src/opt.rs index 5455777..4e6f46d 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -64,6 +64,18 @@ pub enum SubCommand { New { #[structopt(help = "Title of new draft")] title: String, + + #[structopt(long, help = "Add link to a topic page")] + topic: Option, + }, + + /// Create topic page. + NewTopic { + #[structopt(help = "Path to topic page in journal")] + path: PathBuf, + + #[structopt(help = "Title of topic page")] + title: String, }, /// Invoke editor on journal entry draft. diff --git a/src/template.rs b/src/template.rs index 89c1a15..23b9b06 100644 --- a/src/template.rs +++ b/src/template.rs @@ -4,10 +4,22 @@ use tera::{Context, Tera}; const NEW_ENTRY: &str = r#"[[!meta title="{{ title }}"]] [[!meta date="{{ date }}"]] +{% if topic %} +[[!meta link="{{ topic }}"]] +{% endif %} This is the default template. "#; +const NEW_TOPIC: &str = r#"[[!meta title="{{ title }}"]] + +This is the default topic template. + +# Entries + +[[!inline pages="link(.)" archive=yes reverse=yes trail=yes]] +"#; + pub struct Templates { tera: Tera, } @@ -17,6 +29,7 @@ impl Templates { let glob = format!("{}/.config/templates/*", dirname.display()); let mut tera = Tera::new(&glob).expect("Tera::new"); add_default_template(&mut tera, "new_entry", NEW_ENTRY); + add_default_template(&mut tera, "new_topic", NEW_TOPIC); Ok(Self { tera }) } @@ -24,6 +37,10 @@ impl Templates { self.render("new_entry", &context) } + pub fn new_topic(&self, context: &Context) -> Result { + self.render("new_topic", &context) + } + fn render(&self, name: &str, context: &Context) -> Result { match self.tera.render(name, &context) { Ok(s) => Ok(s), diff --git a/subplot/jt.py b/subplot/jt.py index 6ce75b0..d6023b3 100644 --- a/subplot/jt.py +++ b/subplot/jt.py @@ -135,6 +135,13 @@ def draft_contains_string(ctx, dirname=None, draftno=None, pattern=None): assert pattern in data +def draft_links_to_topic(ctx, dirname=None, draftno=None, topic=None): + logging.debug(f"checking draft {draftno} in {dirname} links to {topic!r}") + draft_contains_string( + ctx, dirname=dirname, draftno=draftno, pattern=f'\n[[!meta link="{topic}"]]\n' + ) + + def edit_draft(ctx, dirname=None, draftno=None, text=None): logging.debug(f"editing draft {draftno} in {dirname} to also contain {text!r}") draft = os.path.join(dirname, "drafts", f"{draftno}.md") diff --git a/subplot/jt.yaml b/subplot/jt.yaml index 65bf90e..a3d4f0f 100644 --- a/subplot/jt.yaml +++ b/subplot/jt.yaml @@ -43,6 +43,9 @@ - then: draft {draftno} in {dirname} contains "{pattern:text}" function: draft_contains_string +- then: draft {draftno} in {dirname} links to "{topic}" + function: draft_links_to_topic + - then: there are no journal entries in {dirname} function: journal_has_no_entries -- cgit v1.2.1