Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ledger-insert-effective-date to work with regions. #404

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 55 additions & 22 deletions ledger-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -187,34 +187,67 @@ the balance into that."
nil 'noerr)
(replace-match ""))))))))

(defun ledger-insert-effective-date (&optional date)
(defun ledger-insert-effective-date (&optional date start end)
"Insert effective date `DATE' to the transaction or posting.

If `DATE' is nil, prompt the user a date.
If `DATE' is nil, prompt the user for a date.

Replace the current effective date if there's one in the same
line.

With a prefix argument, remove the effective date."
(interactive)
(if (and (listp current-prefix-arg)
(= 4 (prefix-numeric-value current-prefix-arg)))
(ledger-remove-effective-date)
(let* ((context (car (ledger-context-at-point)))
(date-string (or date (ledger-read-date "Effective date: "))))
(save-restriction
(narrow-to-region (line-beginning-position) (line-end-position))
(cond
((eq 'xact context)
(beginning-of-line)
(re-search-forward ledger-iso-date-regexp)
(when (= (char-after) ?=)
(ledger-remove-effective-date))
(insert "=" date-string))
((eq 'acct-transaction context)
(end-of-line)
(ledger-remove-effective-date)
(insert " ; [=" date-string "]")))))))
With a prefix argument, remove the effective date.

With an active region (`START' and `END' non-nil), insert or
remove for all transactions starting within the region."
(interactive
(if (use-region-p)
(list nil (region-beginning) (region-end))
(list nil nil nil)))

(if (and start end) (ledger-insert-effective-date-region start end date)
(if (and (listp current-prefix-arg)
(= 4 (prefix-numeric-value current-prefix-arg)))
(ledger-remove-effective-date)
(let* ((context (car (ledger-context-at-point)))
(date-string (or date (ledger-read-date "Effective date: "))))
(save-restriction
(narrow-to-region (line-beginning-position) (line-end-position))
(cond
((eq 'xact context)
(beginning-of-line)
(re-search-forward ledger-iso-date-regexp)
(when (= (char-after) ?=)
(ledger-remove-effective-date))
(insert "=" date-string))
((eq 'acct-transaction context)
(end-of-line)
(ledger-remove-effective-date)
(insert " ; [=" date-string "]"))))))))

(defun ledger-insert-effective-date-region (start end &optional date)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of duplicated code between this function and ledger-insert-effective-date. Can they be combined? I would think that just looping over the lines in start..end and calling ledger-insert-effective-date (with the correct current-prefix-arg) should be sufficient.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I managed to reduce it a bit, but there's still a decent amount of duplication.

"Insert effective date `DATE' to all transactions starting within
the region.

If `DATE' is nil, prompt the user for a date.

Replace the current effective date if there is one.

With a prefix argument, remove any effective dates."
(interactive "r")
(let* ((should-remove
(and (listp current-prefix-arg)
(= 4 (prefix-numeric-value current-prefix-arg))))
(date-string
(unless should-remove
(or date (ledger-read-date "Effective date: ")))))
(save-excursion
(setq end (copy-marker end))
(goto-char start)
(while (< (point) end)
(let ((context (car (ledger-context-at-point))))
(when (eq 'xact context)
(ledger-insert-effective-date date-string)))
(forward-line 1)))))

(defun ledger-mode-remove-extra-lines ()
"Get rid of multiple empty lines."
Expand Down
3 changes: 2 additions & 1 deletion ledger-reconcile.el
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
(require 'ledger-exec)
(require 'ledger-navigate)
(require 'ledger-state)
(declare-function ledger-insert-effective-date "ledger-mode" (&optional date))
(declare-function ledger-insert-effective-date "ledger-mode"
(&optional start end date))
(declare-function ledger-read-account-with-prompt "ledger-mode" (prompt))
(declare-function ledger-read-date "ledger-mode" (prompt))

Expand Down
73 changes: 73 additions & 0 deletions test/mode-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,79 @@ http://bugs.ledger-cli.org/show_bug.cgi?id=256"
(call-interactively 'ledger-insert-effective-date))
(should (equal (buffer-string) orig-file-contents)))))

(ert-deftest ledger-mode/test-009 ()
"Baseline test for `ledger-insert-effective-date-region'."
:tags '(mode baseline)

(cl-flet ((file-with-dates (date-1 date-2)
;; A file with two optional effective dates.
(format
"\
2024-01-01%s Grocery Store
Expenses:Groceries $30
Liabilities:Credit Card

2024-01-02%s Grocery Store
Expenses:Groceries $10
Expenses:Tax $1.50
Liabilities:Credit Card -$11.50
"
(if date-1 (concat "=" date-1) "")
(if date-2 (concat "=" date-2) ""))))
(ledger-tests-with-temp-file
(file-with-dates nil nil)

;; With no prefix arg, insert or replace effective date for xacts that
;; start on a line that overlaps the range. With one, remove the
;; effective date.

;; Range fully contained in start line.
(save-excursion
(let ((min (progn (forward-char 1) (point)))
(max (progn (forward-char 1) (point))))
(ledger-insert-effective-date-region min max "2024-03-01")
(should (equal (buffer-string) (file-with-dates "2024-03-01" nil)))))

;; Range doesn't overlap the start of an xact: don't do anything.
(save-excursion
(let ((min (progn (forward-line 1) (point)))
(max (progn (forward-line 3) (point))))
(ledger-insert-effective-date-region min max "2024-03-02")
(should (equal (buffer-string) (file-with-dates "2024-03-01" nil)))))

;; Range overlaps multiple xacts: update them all.
(save-excursion
(let ((min (point))
(max (progn (forward-line 4) (forward-char 1) (point))))
(ledger-insert-effective-date-region min max "2024-03-03")
(should (equal (buffer-string)
(file-with-dates "2024-03-03" "2024-03-03")))))

;; Remove effective date from multiple.
(save-excursion
(let ((min (point))
(max (progn (forward-line 4) (forward-char 1) (point)))
(current-prefix-arg '(4)))
(ledger-insert-effective-date-region min max nil)
(should (equal (buffer-string) (file-with-dates nil nil)))))

;; Add it back to both.
(save-excursion
(let ((min (point))
(max (progn (forward-line 4) (forward-char 1) (point))))
(ledger-insert-effective-date-region min max "2024-03-04")
(should (equal (buffer-string)
(file-with-dates "2024-03-04" "2024-03-04")))))

;; Remove from just the first.
(save-excursion
(let ((min (progn (forward-char 1) (point)))
(max (progn (forward-char 1) (point)))
(current-prefix-arg '(4)))
(ledger-insert-effective-date-region min max nil)
(should (equal (buffer-string)
(file-with-dates nil "2024-03-04"))))))))

(provide 'mode-test)

;;; mode-test.el ends here