branch: elpa/beancount
commit 54730b06597426a35f8ba09d26957f6d9c574052
Author: Fang Lungang <[email protected]>
Commit: Martin Blais <[email protected]>
Add context-aware date adjustment functions
Implement beancount-date-up and beancount-date-down to increment/decrement
date components based on cursor position. When cursor is on the year, adjust
by years; on month, adjust by months; on day, adjust by days. Includes
proper
handling of month boundaries and day overflow (e.g., Jan 31 to Feb 28/29).
---
beancount.el | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/beancount.el b/beancount.el
index 34ceed00257..a71638ca816 100644
--- a/beancount.el
+++ b/beancount.el
@@ -972,6 +972,89 @@ With prefix ARG, change that many days."
(interactive "p")
(beancount--shift-date-at-point (- (or days 1))))
+(defun beancount--get-date-component-at-point ()
+ "Determine which component of the date (year, month, day) the cursor is on.
+Returns `'year', `'month', `'day', or nil if not on a date."
+ (when (thing-at-point-looking-at beancount-date-regexp 10)
+ (let* ((date-start (match-beginning 0))
+ (date-end (match-end 0))
+ (pos (point))
+ (date-string (match-string 0)))
+ (cond
+ ;; Year: positions 0-3
+ ((and (>= pos date-start) (< pos (+ date-start 4)))
+ 'year)
+ ;; Separator after year: position 4
+ ((= pos (+ date-start 4))
+ 'year)
+ ;; Month: positions 5-6
+ ((and (>= pos (+ date-start 5)) (< pos (+ date-start 7)))
+ 'month)
+ ;; Separator after month: position 7
+ ((= pos (+ date-start 7))
+ 'month)
+ ;; Day: positions 8-9
+ ((and (>= pos (+ date-start 8)) (<= pos date-end))
+ 'day)
+ (t nil)))))
+
+(defun beancount--shift-date-component-at-point (increment)
+ "Shift the date component at point by INCREMENT units.
+The component (year, month, or day) is determined by cursor position."
+ (if (thing-at-point-looking-at beancount-date-regexp 10)
+ (let* ((pos (point))
+ (component (beancount--get-date-component-at-point))
+ (date-string (match-string 0))
+ (date (beancount--parse-date date-string))
+ (decoded (decode-time date))
+ (second (nth 0 decoded))
+ (minute (nth 1 decoded))
+ (hour (nth 2 decoded))
+ (day (nth 3 decoded))
+ (month (nth 4 decoded))
+ (year (nth 5 decoded))
+ new-date)
+ (pcase component
+ ('year
+ (setq new-date (beancount--encode-time
+ (list second minute hour day month (+ year
increment) nil -1 nil))))
+ ('month
+ (let* ((new-month (+ month increment))
+ (new-year year)
+ (month-offset (if (> new-month 0)
+ (/ (1- new-month) 12)
+ (/ (- new-month 12) 12))))
+ (setq new-year (+ year month-offset))
+ (setq new-month (- new-month (* month-offset 12)))
+ ;; Handle day overflow (e.g., Jan 31 -> Feb 31 should become Feb
28/29)
+ (let ((max-day (calendar-last-day-of-month new-month new-year)))
+ (when (> day max-day)
+ (setq day max-day)))
+ (setq new-date (beancount--encode-time
+ (list second minute hour day new-month new-year
nil -1 nil)))))
+ ('day
+ (setq new-date (beancount--encode-time
+ (list second minute hour (+ day increment) month
year nil -1 nil))))
+ (_
+ (user-error "Could not determine date component at point")))
+ (replace-match (beancount--format-date new-date) t t)
+ (goto-char pos))
+ (user-error "No date at point")))
+
+(defun beancount-date-up (&optional n)
+ "Increase the date component at point by N units (default 1).
+If cursor is on year, increment year; on month, increment month; on day,
+increment day."
+ (interactive "p")
+ (beancount--shift-date-component-at-point (or n 1)))
+
+(defun beancount-date-down (&optional n)
+ "Decrease the date component at point by N units (default 1).
+If cursor is on year, decrement year; on month, decrement month; on day,
+decrement day."
+ (interactive "p")
+ (beancount--shift-date-component-at-point (- (or n 1))))
+
(defvar beancount-install-dir nil
"Directory in which Beancount's source is located.
Only useful if you have not installed Beancount properly in your PATH.")