In a ledger-mode or report-moderegister buffer, place the cursor on a transaction and type: M-x ledger-edit-fn or C-M-s-RET [with the s being the super / Command key on Apple keyboards]
In the narrowed indirect buffer, do your editing and when done, type: M-x ledger-edit-finalize or C-c C-c
When calling ledger-edit-finalize, you will be transported back to the initial buffer -- if it was a report buffer and if it was modified, the new report will be generated automatically due to the save-buffer command baked into the aforementioned finalize command.
I use a custom register format that is the following, and the ledger-edit-register-report-regexp is based on this:
(let ((register-format
(concat "\"%(!cleared"
" ? ansify_if (date, bold)"
" : ansify_if (date, blue if color))"
" %(ansify_if (justify (truncated (display_account, int(account_width), int(abbrev_len)), int(account_width)),"
" white if color))"
" %((display_amount < 0)"
" ? (!cleared"
" ? ansify_if (justify (scrub (display_amount), 13, -1, true, color), bold)"
" : justify (scrub (display_amount), 13, -1, true, color))"
" : ansify_if (justify (scrub (display_amount), 13, -1, true, color), black if color))"
" %((display_total < 0)"
" ? justify (scrub (display_total), 13, -1, true, color)"
" : ansify_if (justify (scrub (display_total), 13, -1, true, color), black if color))"
" %(cleared"
" ? ansify_if (\\\"*\\\", yellow if color)"
" : (pending"
" ? ansify_if (\\\"!\\\", magenta if color)"
" : \\\" \\\"))"
" %(ansify_if (payee, blink if color and !cleared and actual))\n\n\"")))
[INSERT CODE TO CREATE THE LEDGER COMMAND LINE WITH ARGS])
The following code is brand new and only very lightly tested. Feel free to use anything, whether it be just the concept or some/all of the code. I don't plan on making a public package, but will continue to work on perfecting this in my own setup as I use it for myself. The positions of the cursor that are tracked and potentially restored when switching buffers are: (1) date, (2) transaction amount, (3) payment status, and (4) payee. When the cursor is on another location (e.g., a line containing an account name), then point will be placed either at the beginning of the entity when going to a different buffer or stay right where it was.
(define-key ledger-mode-map (kbd "<C-M-s-return>") 'ledger-edit-fn)
(define-key ledger-report-mode-map (kbd "<C-M-s-return>") 'ledger-edit-fn)
(define-key ledger-report-mode-map [(control ?c) (control ?c)] 'ledger-edit-toggle-status)
;;; Fix for the infinity loop: https://github.com/ledger/ledger-mode/issues/274
(defun ledger-toggle-current (&optional style)
"Toggle the current thing at point with optional STYLE."
(interactive)
(if (or ledger-clear-whole-transactions
(eq 'transaction (ledger-thing-at-point)))
(let ((point-a -1)
(point-b 0))
(save-excursion
(forward-line)
(goto-char (line-beginning-position))
(while (and (not (= point-a point-b))
(not (eolp))
(save-excursion
(not (eq 'transaction (ledger-thing-at-point)))))
(if (looking-at "\\s-+[*!]")
(ledger-toggle-current-posting style))
(setq point-a (point))
(forward-line)
(goto-char (line-beginning-position))
(setq point-b (point))))
(ledger-toggle-current-transaction style))
(ledger-toggle-current-posting style)))
;;; 3-Way Toggle of Status: https://github.com/ledger/ledger-mode/issues/283
(defun ledger-toggle-current-transaction (&optional style)
"Toggle the transaction at point using optional STYLE."
(interactive)
(save-excursion
(when (or (looking-at "^[0-9]")
(re-search-backward "^[0-9]" nil t))
(skip-chars-forward "0-9./=\\-")
(delete-horizontal-space)
(let ((current-state (ledger-state-from-char (char-after))))
(when (or (eq current-state 'pending)
(eq current-state 'cleared))
(delete-char 1))
(cond
((and style (eq style 'cleared))
(insert " *")
'cleared)
((and style (eq style 'pending))
(insert " ! ")
'pending)
((eq current-state 'cleared)
(insert "")
'uncleared)
((eq current-state nil) ;; 'uncleared
(insert " ! ")
'pending)
((eq current-state 'pending)
(insert " *")
'cleared))))))
(defgroup ledger-edit nil
"This is the ledger-edit-fn group doc-string."
:tag "Ledger Edit"
:group 'ledger)
(defvar ledger-edit-display-method (list 'split-window-below 'below '((window-height . 20)))
"Expected value is one of the following:
(list 'split-window-below direction alist)
(list 'switch-to-buffer nil nil)")
(defvar ledger-payee-regexp
"^\\(?:\\(\\([=~]\\).*\\)\\|\\([[:digit:]][^ \t\n]*\\)\\(?:[ \t]+\\([*!]\\)\\)?\\(?:[ \t]+\\((.*?)\\)\\)?\\(?:[ \t]+\\(.+?\\)\\)?\\(?:\\(?:\t\\|[ \t]\\{2,\\}\\)\\(;[^\n]*\\)\\)?$\\)"
"A regexp that matches the entire payee line. Extracted from the variable
`ledger-font-lock-keywords' that is used by `font-lock-keywords'.")
(defvar ledger-edit-register-report-regexp
"^\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)[\s]+\\([a-zA-Z0-9.:_-]+\\)[\s]+\\([$-.,0-9]+\\)[\s]+\\([$-.,0-9]+\\)[\s]+\\([*!]+\\)?[\s]+\\(.*\\)$"
"A regexp that matches each line of the register report. The following groups
are expected to be present: date, display amount, payment status, and payee.")
(defvar ledger-edit-account-regexp "^[\s]+\\([a-zA-Z0-9.:_,\s-]+[^\s0-9(]\\)\\([\s]+\\([$0-9,.()-*]+\\).*\\)?$"
"A regexp that matches the account line underneath the payee line.")
(defvar ledger-edit-variable nil
"A cons cell -- `car` = `initial-buffer`; and `cdr` = `ledger-file-buffer`.")
(make-variable-buffer-local 'ledger-edit-variable)
(defvar ledger-edit-prefix-key [(control ?c)]
"The common prefix key used in ledger-edit-fn mode.")
(defvar ledger-edit-prefix-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c" 'ledger-edit-finalize)
map)
"The key bindings provided in ledger-edit-fn mode.")
(defvar ledger-edit-mode-map
(let ((map (make-sparse-keymap)))
(define-key map ledger-edit-prefix-key ledger-edit-prefix-map)
(define-key map (kbd "C-S-c C-S-c") #'ledger-toggle-current)
(define-key map [?\M-w] (lambda ()
(interactive)
(if (y-or-n-p "This is an indirect buffer. Widen anyway?")
(widen)
(message "You have chosen to exit this function. Goodbye!"))))
(define-key map [?\s-s] (lambda ()
(interactive)
(let* ((ledger-edit-buffer (current-buffer))
(initial-buffer
(with-current-buffer ledger-edit-buffer
(car ledger-edit-variable)))
(ledger-file-buffer
(with-current-buffer ledger-edit-buffer
(cdr ledger-edit-variable))))
(if (and (eq initial-buffer (get-buffer ledger-report-buffer-name))
(buffer-live-p ledger-file-buffer))
(if (y-or-n-p "Save + Kill Indirect + Generate Report?")
(progn
(save-buffer)
(kill-buffer ledger-edit-buffer))
(message "You have chosen to exit this function. Goodbye!"))
(save-buffer)))))
(define-key map [M-up] 'backward-paragraph)
(define-key map [M-down] 'forward-paragraph)
map)
"Keymap to use in `ledger-edit-mode'.")
(defun ledger-edit-narrow-to-entity (initial-buffer target-group target-pos &optional toggle-status)
"Narrow buffer to the current subtree."
(interactive)
(let* ((exts (ledger-navigate-find-element-extents (point)))
(beg (car exts))
(end (cadr exts))
(pt (point))
(ledger-file-buffer (current-buffer))
(indirect-buffer-name
(cond
((not (and (> (- end beg) 1) ; not an empty line
(<= pt end) (>= pt beg) ; point is within the boundaries
(not (region-active-p)))) ; no active region
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-narrow-to-entity: Ooops -- beg (%s) | end (%s)" beg end)))
(signal 'quit `(,quit-message))))
((get-buffer (format "*%s*" ledger-file-buffer))
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-narrow-to-entity: (%s) is already in use!" (format "*%s*" ledger-file-buffer))))
(signal 'quit `(,quit-message))))
((buffer-base-buffer)
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-narrow-to-entity:: (%s) is an indirect buffer!" (current-buffer))))
(signal 'quit `(,quit-message))))
(t
(format "*%s*" ledger-file-buffer))))
(indirect-buffer (make-indirect-buffer ledger-file-buffer indirect-buffer-name 'clone)))
(cond
((and (null toggle-status)
(eq (nth 0 ledger-edit-display-method) 'split-window-below))
(let* ((direction (nth 1 ledger-edit-display-method))
(alist (nth 2 ledger-edit-display-method))
(target-window
(cond
((get-buffer-window indirect-buffer (selected-frame)))
((window-in-direction direction))
(t
(split-window (selected-window) nil direction)))))
(window--display-buffer indirect-buffer target-window 'window alist)
(select-window target-window)))
((and (null toggle-status)
(eq (nth 0 ledger-edit-display-method) 'switch-to-buffer))
(switch-to-buffer indirect-buffer))
(toggle-status
(set-buffer indirect-buffer))
(t
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-narrow-to-entity:: (%s) is not an expected display-method!" ledger-edit-display-method)))
(signal 'quit `(,quit-message)))))
(setq ledger-edit-variable (cons initial-buffer ledger-file-buffer))
;; overlay removal works best if done before narrowing to region, because line numbers change
;; and this caused a problem when left over overlays (invisible to the human eye) remained in
;; the buffer and caused a conflict with line numbers with +-mode.
(narrow-to-region beg end)
(ledger-edit-mode 1)
(when (and (not (null target-group))
(not (null target-pos)))
(goto-char (point-min))
(when (looking-at ledger-payee-regexp)
(let ((indirect-buffer--date-beg (match-beginning 3))
(indirect-buffer--status-beg (match-beginning 4))
(indirect-buffer--payee-beg (match-beginning 6)))
(cond
((and (not (null indirect-buffer--date-beg))
(eq target-group 'date))
(goto-char (+ indirect-buffer--date-beg target-pos)))
((eq target-group 'amount)
(forward-line 1)
(when (looking-at ledger-edit-account-regexp)
;;; . Subtract the dollar sign which is included in the report buffer,
;;; but excluded from both the master ledger file and the indirect buffer.
;;; . When looking at an automatic calculation in parentheses, stop there.
;;; . When target-pos is 0, do not subtract anything.
;;; . _must_ use `save-match-data' here!
;;; . FIXME: When the report buffer has a negative amount, there is an extra character that needs to be dealt with.
(cond ((save-match-data (string-match "^\(" (match-string 3)))
(goto-char (match-beginning 3)))
((= target-pos 0)
(goto-char (match-beginning 3)))
(t
(goto-char (+ (match-beginning 3) (1- target-pos)))))))
((and (not (null indirect-buffer--status-beg))
(eq target-group 'status))
(goto-char (+ indirect-buffer--status-beg target-pos)))
((and (not (null indirect-buffer--payee-beg))
(eq target-group 'payee))
(goto-char (+ indirect-buffer--payee-beg target-pos)))
(t
(message "ledger-edit-narrow-to-entity: catch-all (no match found)"))))))
(when (not (null toggle-status))
;;; @lawlist is unsure what the usage would be to toggle status on a line
;;; containing an account, rather than a line containing the payee.
;;; As such, the default behavior here is to place point at the beginning
;;; of the transaction.
(save-excursion
(goto-char (point-min))
(ledger-toggle-current nil))
(ledger-edit-finalize 'toggle-status))))
(defun ledger-edit-fn (&optional toggle-status)
(interactive)
(let ((initial-buffer (current-buffer))
target-group target-pos)
(cond
((equal initial-buffer (get-buffer ledger-report-buffer-name))
(let* ((ledger-init-pos (point))
(prop (get-text-property (point) 'ledger-source))
(file (car prop))
(line (cdr prop))
(ledger-file-buffer (and file (get-file-buffer file))))
(cond
((null ledger-file-buffer)
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-fn: buffer of file (%s) cannot be located!" file)))
(signal 'quit `(,quit-message))))
((not (and file line))
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-fn: Cannot locate both file (%s) and line (%s)!" file line)))
(signal 'quit `(,quit-message))))
((= line 1)
(when (not (y-or-n-p "ledger-edit-fn: line = 1, which may indicate a problem. Proceed?"))
(let ((debug-on-quit nil))
(signal 'quit '("ledger-edit-fn: You have chosen to a abort."))))))
(save-excursion
(goto-char (line-beginning-position))
(when (looking-at ledger-edit-register-report-regexp)
(let ((report-buffer--date (match-string 1))
(report-buffer--date-beg (match-beginning 1))
(report-buffer--date-end (match-end 1))
(report-buffer--amount (match-string 3))
(report-buffer--amount-beg (match-beginning 3))
(report-buffer--amount-end (match-end 3))
(report-buffer--status (match-string 5))
(report-buffer--status-beg (match-beginning 5))
(report-buffer--status-end (match-end 5))
(report-buffer--payee (match-string 6))
(report-buffer--payee-beg (match-beginning 6))
(report-buffer--payee-end (match-end 6)))
;;; We do something useless with unused let-bound variables so
;;; that we can have them handy for potential future use and/or
;;; debugging, but to silence the byte-compiler.
(list report-buffer--payee report-buffer--status report-buffer--amount report-buffer--date)
(cond
((and (not (null report-buffer--date-beg))
(not (null report-buffer--date-end))
(>= ledger-init-pos report-buffer--date-beg)
(< ledger-init-pos report-buffer--date-end))
(setq target-group 'date
target-pos (- ledger-init-pos report-buffer--date-beg)))
((and (not (null report-buffer--amount-beg))
(not (null report-buffer--amount-end))
(>= ledger-init-pos report-buffer--amount-beg)
(< ledger-init-pos report-buffer--amount-end))
(setq target-group 'amount
target-pos (- ledger-init-pos report-buffer--amount-beg)))
((and (not (null report-buffer--status-beg))
(not (null report-buffer--status-end))
(>= ledger-init-pos report-buffer--status-beg)
(< ledger-init-pos report-buffer--status-end))
(setq target-group 'status
target-pos (- ledger-init-pos report-buffer--status-beg)))
((and (not (null report-buffer--payee-beg))
(not (null report-buffer--payee-end))
(>= ledger-init-pos report-buffer--payee-beg)
(< ledger-init-pos report-buffer--payee-end))
(setq target-group 'payee
target-pos (- ledger-init-pos report-buffer--payee-beg)))
(t
(message "ledger-edit-fn: catch-all (no match found)"))))))
;;; Since the report buffer still has focus, we need to temporarily
;;; work with the ledger-file-buffer.
(with-current-buffer ledger-file-buffer
(widen)
(goto-char (point-min))
(forward-line (1- line))
(ledger-edit-narrow-to-entity initial-buffer target-group target-pos toggle-status))))
(t
(ledger-edit-narrow-to-entity initial-buffer target-group target-pos toggle-status)))))
(defun ledger-edit-finalize (&optional toggle-status)
(interactive)
(let* ((ledger-init-pos (point))
(ledger-edit-buffer (current-buffer))
(initial-buffer (with-current-buffer ledger-edit-buffer (car ledger-edit-variable)))
(ledger-file-buffer (with-current-buffer ledger-edit-buffer (cdr ledger-edit-variable)))
(orig-buffer-modified (buffer-modified-p ledger-file-buffer))
(payee-matched-data-p
(save-excursion
(goto-char (point-min))
(looking-at ledger-payee-regexp)))
(indirect-buffer--date (and payee-matched-data-p (match-string 3)))
(indirect-buffer--date-beg (and payee-matched-data-p (match-beginning 3)))
(indirect-buffer--date-end (and payee-matched-data-p (match-end 3)))
(indirect-buffer--status (and payee-matched-data-p (match-string 4)))
(indirect-buffer--status-beg (and payee-matched-data-p (match-beginning 4)))
(indirect-buffer--status-end (and payee-matched-data-p (match-end 4)))
(indirect-buffer--payee (and payee-matched-data-p (match-string 6)))
(indirect-buffer--payee-beg (and payee-matched-data-p (match-beginning 6)))
(indirect-buffer--payee-end (and payee-matched-data-p (match-end 6)))
(account-matched-data-p
(save-excursion
(goto-char (point-min))
(forward-line 1)
(looking-at ledger-edit-account-regexp)))
(indirect-buffer--amount (and account-matched-data-p (match-string 3)))
(indirect-buffer--amount-beg (and account-matched-data-p (match-beginning 3)))
(indirect-buffer--amount-end (and account-matched-data-p (match-end 3)))
target-group target-pos)
(cond
((and (not (null indirect-buffer--date-beg))
(not (null indirect-buffer--date-end))
(>= ledger-init-pos indirect-buffer--date-beg)
(< ledger-init-pos indirect-buffer--date-end))
(setq target-group 'date
target-pos (- ledger-init-pos indirect-buffer--date-beg)))
((and (not (null indirect-buffer--amount-beg))
(not (null indirect-buffer--amount-end))
(>= ledger-init-pos indirect-buffer--amount-beg)
(< ledger-init-pos indirect-buffer--amount-end))
(setq target-group 'amount
target-pos (- ledger-init-pos indirect-buffer--amount-beg)))
((and (not (null indirect-buffer--status-beg))
(not (null indirect-buffer--status-end))
(>= ledger-init-pos indirect-buffer--status-beg)
(< ledger-init-pos indirect-buffer--status-end))
(setq target-group 'status
target-pos (- ledger-init-pos indirect-buffer--status-beg)))
((and (not (null indirect-buffer--payee-beg))
(not (null indirect-buffer--payee-end))
(>= ledger-init-pos indirect-buffer--payee-beg)
(< ledger-init-pos indirect-buffer--payee-end))
(setq target-group 'payee
target-pos (- ledger-init-pos indirect-buffer--payee-beg))))
(when ledger-edit-buffer
(cond
((and (null toggle-status)
(eq (nth 0 ledger-edit-display-method) 'split-window-below))
(delete-window))
((and (null toggle-status)
(eq (nth 0 ledger-edit-display-method) 'switch-to-buffer)
(message "ledger-edit-finalize: Maybe write some code here.")))
(toggle-status
(message "ledger-edit-finalize: toggle-status is non-nil."))
(t
(let ((debug-on-quit nil)
(quit-message (format "ledger-edit-finalize: (%s) is not an expected display-method!" ledger-edit-display-method)))
(signal 'quit `(,quit-message)))))
(kill-buffer ledger-edit-buffer))
(if (and (eq initial-buffer (get-buffer ledger-report-buffer-name))
(buffer-live-p ledger-file-buffer))
(let* ((ledger-edit-after-save-fn (list (lambda ()
(unless (or (derived-mode-p 'ledger-mode)
(derived-mode-p 'ledger-report-mode))
(user-error "Not in a ledger-mode or ledger-report-mode buffer"))
(when (and ledger-report-auto-refresh
(get-buffer ledger-report-buffer-name))
(with-current-buffer (get-buffer ledger-report-buffer-name)
(with-silent-modifications
(erase-buffer)
(ledger-do-report ledger-report-cmd)
(when ledger-report-is-reversed
(ledger-report-reverse-lines))))
(set-window-buffer (selected-window) (get-buffer ledger-report-buffer-name))
(run-hooks 'ledger-report-after-report-hook)))))
(report-buffer-finalize-fn `(lambda ()
(with-current-buffer (get-buffer ledger-report-buffer-name)
(catch 'done
(goto-char (point-min))
(while (re-search-forward (regexp-quote ,indirect-buffer--payee) nil t)
(when (save-excursion
(goto-char (line-beginning-position))
(looking-at ledger-edit-register-report-regexp))
(let ((report-buffer--date (match-string 1))
(report-buffer--date-beg (match-beginning 1))
(report-buffer--date-end (match-end 1))
(report-buffer--amount (match-string 3))
(report-buffer--amount-beg (match-beginning 3))
(report-buffer--amount-end (match-end 3))
(report-buffer--status (match-string 5))
(report-buffer--status-beg (match-beginning 5))
(report-buffer--status-end (match-end 5))
(report-buffer--payee (match-string 6))
(report-buffer--payee-beg (match-beginning 6))
(report-buffer--payee-end (match-end 6)))
(when (and (equal ,indirect-buffer--date report-buffer--date)
(equal ,indirect-buffer--status report-buffer--status)
(equal ,indirect-buffer--payee report-buffer--payee))
(cond
((and (not (null report-buffer--date-beg))
(eq ',target-group 'date))
(goto-char (+ report-buffer--date-beg ,target-pos)))
((and (not (null report-buffer--amount-beg))
(eq ',target-group 'amount))
(if (equal ,indirect-buffer--amount report-buffer--amount)
(goto-char (+ report-buffer--amount-beg ,target-pos))
(goto-char report-buffer--amount-beg)))
((and (not (null report-buffer--status-beg))
(eq ',target-group 'status))
(goto-char (+ report-buffer--status-beg ,target-pos)))
((and (not (null report-buffer--payee-beg))
(eq ',target-group 'payee))
(goto-char (+ report-buffer--payee-beg ,target-pos)))
(t
(goto-char (match-beginning 0))
(message "ledger-edit-finalize: catch-all (no match found)")))
(recenter)
(throw 'done nil)))))))))
(ledger-report-after-report-hook (list report-buffer-finalize-fn)))
(if orig-buffer-modified
(with-current-buffer ledger-file-buffer
;;; Because `ledger-mode.el' locally binds `ledger-report-redo' to
;;; the `after-save-hook', the override needs to be local too.
(let ((after-save-hook ledger-edit-after-save-fn))
(save-buffer)))
(run-hooks 'ledger-report-after-report-hook)))
;;; ELSE: We are not dealing with an _initial_ report-buffer situation.
(with-current-buffer ledger-file-buffer
;;; Because `ledger-mode.el' locally binds `ledger-report-redo' to
;;; the `after-save-hook', the override needs to be local too.
(when orig-buffer-modified
(save-buffer))
;;; FIXME: When NULL target-group/pos, would it be preferable to leave
;;; point in the file buffer right where it was at the outset, or instead
;;; goto to the beginning of the xact? If the latter, then _move_
;;; `ledger-navigate-beginning-of-xact' to just below this comment.
(if (or (null target-group)
(null target-pos))
(message "ledger-edit-finalize: target-group (%s) | target-pos (%s)" target-group target-pos)
(ledger-navigate-beginning-of-xact)
(when (looking-at ledger-payee-regexp)
(let ((file-buffer--date-beg (match-beginning 3))
(file-buffer--status-beg (match-beginning 4))
(file-buffer--payee-beg (match-beginning 6)))
(cond
((and (not (null file-buffer--date-beg))
(eq target-group 'date))
(goto-char (+ file-buffer--date-beg target-pos)))
((eq target-group 'amount)
(forward-line 1)
(when (looking-at ledger-edit-account-regexp)
(goto-char (+ (match-beginning 3) target-pos))))
((and (not (null file-buffer--status-beg))
(eq target-group 'status))
(goto-char (+ file-buffer--status-beg target-pos)))
((and (not (null file-buffer--payee-beg))
(eq target-group 'payee))
(goto-char (+ file-buffer--payee-beg target-pos)))
(t
(message "ledger-edit-finalize: catch-all (no match found)"))))))))))
(defun ledger-edit-toggle-status ()
"From the report buffer, toggle the status of an entry at point."
(interactive)
(ledger-edit-fn 'toggle-status))
(define-minor-mode ledger-edit-mode
"By virtue of `(make-indirect-buffer buffer bname 'clone)` within `ledger-edit-fn`,
the argument `'clone` keeps the existing settings -- i.e., `ledger-mode` with ...."
:init-value nil
:lighter " ledger-edit-fn"
:keymap ledger-edit-mode-map
:global nil
:group 'ledger-edit-fn
(cond
(ledger-edit-mode
(setq buffer-read-only nil)
(setq truncate-lines nil)
(when (called-interactively-p 'any)
(message "Turned ON `ledger-edit-mode'.")))
(t
(when (called-interactively-p 'any)
(message "Turned OFF `ledger-edit-mode'.")))))
In a
ledger-mode
orreport-mode
register buffer, place the cursor on a transaction and type:M-x ledger-edit-fn
orC-M-s-RET
[with thes
being thesuper
/Command
key on Apple keyboards]In the narrowed indirect buffer, do your editing and when done, type:
M-x ledger-edit-finalize
orC-c C-c
When calling
ledger-edit-finalize
, you will be transported back to the initial buffer -- if it was a report buffer and if it was modified, the new report will be generated automatically due to thesave-buffer
command baked into the aforementioned finalize command.I use a custom register format that is the following, and the
ledger-edit-register-report-regexp
is based on this:The following code is brand new and only very lightly tested. Feel free to use anything, whether it be just the concept or some/all of the code. I don't plan on making a public package, but will continue to work on perfecting this in my own setup as I use it for myself. The positions of the cursor that are tracked and potentially restored when switching buffers are: (1) date, (2) transaction amount, (3) payment status, and (4) payee. When the cursor is on another location (e.g., a line containing an account name), then point will be placed either at the beginning of the entity when going to a different buffer or stay right where it was.