feat(diary): restore diary_start_week_day via configurable weekly naming
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 34s
CI / cargo test (push) Successful in 31s
CI / editor keymaps (push) Successful in 1m34s

c63ec67 dropped diary_start_week_day, hardwiring the weekly diary to ISO
(Monday) weeks. Upstream vimwiki instead names a weekly note by the
week-start day's date (YYYY-MM-DD) and honours diary_start_week_day. Rather
than force one scheme, make it a per-wiki choice so migrators keep upstream
behaviour while existing nuwiki weekly files keep working:

- New per-wiki key `diary_weekly_style`: `iso` (default — `YYYY-Www`,
  Monday-based, nuwiki's original) or `date`/`vimwiki` (`YYYY-MM-DD` of the
  week-start day, upstream parity).
- Restored per-wiki key `diary_start_week_day` (`monday`..`sunday`, default
  monday); applies only in `date` mode.

Implementation:
- nuwiki-core::date gains WeeklyStyle, WeekStart, and DiaryCalendar (owns
  today/next/prev — date-mode snaps to the week-start and steps ±7 days;
  iso/daily/monthly/yearly defer to the existing DiaryPeriod logic).
- WikiConfig gains the two fields (+ defaults, RawWiki, From) and a
  diary_calendar() builder; commands.rs diary_open_relative and the
  next/prev pivot use it.

Defaults preserve current behaviour (iso/monday), so no breaking change.
Tests: DiaryCalendar cases in nuwiki-core/tests/diary.rs (snap, ±7, sunday
start, iso delegation, daily) + config round-trip in
nuwiki-lsp/tests/index_and_config.rs. README + doc/nuwiki.txt + lua config
comment updated. fmt + clippy clean; all crate tests + config-parity green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 17:06:09 +00:00
parent 28d5caf581
commit b8586537f8
9 changed files with 318 additions and 14 deletions
+95 -1
View File
@@ -1,7 +1,9 @@
//! Cluster 4 — diary frequency / period primitives.
//! Tests the date math for ISO weeks, monthly, yearly periods.
use nuwiki_core::date::{DiaryDate, DiaryFrequency, DiaryPeriod};
use nuwiki_core::date::{
DiaryCalendar, DiaryDate, DiaryFrequency, DiaryPeriod, WeekStart, WeeklyStyle,
};
#[test]
fn frequency_parses_canonical_strings() {
@@ -182,3 +184,95 @@ fn day_next_and_prev_still_work() {
DiaryPeriod::Day(DiaryDate::from_ymd(2026, 5, 11).unwrap())
);
}
// ===== DiaryCalendar — weekly naming styles + week-start =====
// 2026-05-25 is a Monday (so 2026-05-27 is a Wednesday, 2026-05-24 a Sunday).
fn day(y: i32, m: u8, d: u8) -> DiaryPeriod {
DiaryPeriod::Day(DiaryDate::from_ymd(y, m, d).unwrap())
}
#[test]
fn weekly_style_and_week_start_parse() {
assert_eq!(WeeklyStyle::parse("date"), WeeklyStyle::Date);
assert_eq!(WeeklyStyle::parse("vimwiki"), WeeklyStyle::Date);
assert_eq!(WeeklyStyle::parse("iso"), WeeklyStyle::Iso);
assert_eq!(WeeklyStyle::parse(""), WeeklyStyle::Iso); // default
assert_eq!(WeeklyStyle::parse("WEEK"), WeeklyStyle::Iso);
// WeekStart::parse is exercised via the calendar tests below; unknown
// names fall back to Monday.
assert_eq!(WeekStart::parse("sunday"), WeekStart::parse("SUNDAY"));
assert_eq!(WeekStart::parse("nonsense"), WeekStart::monday());
}
#[test]
fn date_mode_weekly_today_is_a_day() {
let cal = DiaryCalendar::new(
DiaryFrequency::Weekly,
WeeklyStyle::Date,
WeekStart::monday(),
);
assert!(matches!(cal.today(), DiaryPeriod::Day(_)));
}
#[test]
fn iso_mode_weekly_today_is_a_week() {
let cal = DiaryCalendar::new(
DiaryFrequency::Weekly,
WeeklyStyle::Iso,
WeekStart::monday(),
);
assert!(matches!(cal.today(), DiaryPeriod::Week { .. }));
}
#[test]
fn date_mode_weekly_steps_seven_days_from_the_week_start_monday() {
let cal = DiaryCalendar::new(
DiaryFrequency::Weekly,
WeeklyStyle::Date,
WeekStart::monday(),
);
// From a Wednesday: snap back to Mon 05-25, then ±7.
assert_eq!(cal.next(day(2026, 5, 27)), day(2026, 6, 1));
assert_eq!(cal.prev(day(2026, 5, 27)), day(2026, 5, 18));
// From the Monday itself: no snap, just ±7.
assert_eq!(cal.next(day(2026, 5, 25)), day(2026, 6, 1));
assert_eq!(cal.prev(day(2026, 5, 25)), day(2026, 5, 18));
// Stem is the plain date, like upstream.
assert_eq!(cal.next(day(2026, 5, 27)).format(), "2026-06-01");
}
#[test]
fn date_mode_weekly_honours_a_sunday_week_start() {
let cal = DiaryCalendar::new(
DiaryFrequency::Weekly,
WeeklyStyle::Date,
WeekStart::parse("sunday"),
);
// Wed 05-27 snaps back to Sun 05-24, +7 = Sun 05-31.
assert_eq!(cal.next(day(2026, 5, 27)), day(2026, 5, 31));
assert_eq!(cal.prev(day(2026, 5, 27)), day(2026, 5, 17));
}
#[test]
fn iso_mode_weekly_delegates_to_iso_week_stepping() {
let cal = DiaryCalendar::new(
DiaryFrequency::Weekly,
WeeklyStyle::Iso,
WeekStart::monday(),
);
let wk = DiaryPeriod::week_containing(DiaryDate::from_ymd(2026, 5, 27).unwrap());
assert_eq!(cal.next(wk), wk.next());
assert_eq!(cal.prev(wk), wk.prev());
}
#[test]
fn daily_calendar_steps_one_day_regardless_of_weekly_fields() {
let cal = DiaryCalendar::new(
DiaryFrequency::Daily,
WeeklyStyle::Date,
WeekStart::parse("sunday"),
);
assert_eq!(cal.next(day(2026, 5, 27)), day(2026, 5, 28));
assert_eq!(cal.prev(day(2026, 5, 27)), day(2026, 5, 26));
}