ledger-state.el 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. ;;; ledger-state.el --- Helper code for use with the "ledger" command-line tool
  2. ;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org)
  3. ;; This file is not part of GNU Emacs.
  4. ;; This is free software; you can redistribute it and/or modify it under
  5. ;; the terms of the GNU General Public License as published by the Free
  6. ;; Software Foundation; either version 2, or (at your option) any later
  7. ;; version.
  8. ;;
  9. ;; This is distributed in the hope that it will be useful, but WITHOUT
  10. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. ;; for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  16. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  17. ;; MA 02110-1301 USA.
  18. ;;; Commentary:
  19. ;; Utilities for dealing with transaction and posting status.
  20. ;;; Code:
  21. (defcustom ledger-clear-whole-transactions nil
  22. "If non-nil, clear whole transactions, not individual postings."
  23. :type 'boolean
  24. :group 'ledger)
  25. (defun ledger-transaction-state ()
  26. "Return the state of the transaction at point."
  27. (save-excursion
  28. (when (or (looking-at "^[0-9]")
  29. (re-search-backward "^[0-9]" nil t))
  30. (skip-chars-forward "0-9./=\\-")
  31. (skip-syntax-forward " ")
  32. (cond ((looking-at "!\\s-*") 'pending)
  33. ((looking-at "\\*\\s-*") 'cleared)
  34. (t nil)))))
  35. (defun ledger-posting-state ()
  36. "Return the state of the posting."
  37. (save-excursion
  38. (goto-char (line-beginning-position))
  39. (skip-syntax-forward " ")
  40. (cond ((looking-at "!\\s-*") 'pending)
  41. ((looking-at "\\*\\s-*") 'cleared)
  42. (t (ledger-transaction-state)))))
  43. (defun ledger-char-from-state (state)
  44. "Return the char representation of STATE."
  45. (if state
  46. (if (eq state 'pending)
  47. "!"
  48. "*")
  49. ""))
  50. (defun ledger-state-from-char (state-char)
  51. "Get state from STATE-CHAR."
  52. (cond ((eql state-char ?\!) 'pending)
  53. ((eql state-char ?\*) 'cleared)
  54. ((eql state-char ?\;) 'comment)
  55. (t nil)))
  56. (defun ledger-state-from-string (state-string)
  57. "Get state from STATE-CHAR."
  58. (when state-string
  59. (cond
  60. ((string-match "\\!" state-string) 'pending)
  61. ((string-match "\\*" state-string) 'cleared)
  62. ((string-match ";" state-string) 'comment)
  63. (t nil))))
  64. (defun ledger-toggle-current-posting (&optional style)
  65. "Toggle the cleared status of the transaction under point.
  66. Optional argument STYLE may be `pending' or `cleared', depending
  67. on which type of status the caller wishes to indicate (default is
  68. `cleared'). Returns the new status as 'pending 'cleared or nil.
  69. This function is rather complicated because it must preserve both
  70. the overall formatting of the ledger xact, as well as ensuring
  71. that the most minimal display format is used. This could be
  72. achieved more certainly by passing the xact to ledger for
  73. formatting, but doing so causes inline math expressions to be
  74. dropped."
  75. (interactive)
  76. (let ((bounds (ledger-navigate-find-xact-extents (point)))
  77. new-status cur-status)
  78. ;; Uncompact the xact, to make it easier to toggle the
  79. ;; transaction
  80. (save-excursion ;; this excursion checks state of entire
  81. ;; transaction and unclears if marked
  82. (goto-char (car bounds)) ;; beginning of xact
  83. (skip-chars-forward "0-9./=\\-") ;; skip the date
  84. (skip-chars-forward " \t") ;; skip the white space after the date
  85. (setq cur-status (and (member (char-after) '(?\* ?\!))
  86. (ledger-state-from-char (char-after))))
  87. ;;if cur-status if !, or * then delete the marker
  88. (when cur-status
  89. (let ((here (point)))
  90. (skip-chars-forward "*! ")
  91. (let ((width (- (point) here)))
  92. (when (> width 0)
  93. (delete-region here (point))
  94. (if (search-forward " " (line-end-position) t)
  95. (insert (make-string width ? ))))))
  96. (forward-line)
  97. ;; Shift the cleared/pending status to the postings
  98. (while (looking-at "[ \t]")
  99. (skip-chars-forward " \t")
  100. (when (not (eq (ledger-state-from-char (char-after)) 'comment))
  101. (insert (ledger-char-from-state cur-status) " ")
  102. (if (and (search-forward " " (line-end-position) t)
  103. (looking-at " "))
  104. (delete-char 2)))
  105. (forward-line))
  106. (setq new-status nil)))
  107. ;;this excursion toggles the posting status
  108. (save-excursion
  109. (setq inhibit-modification-hooks t)
  110. (goto-char (line-beginning-position))
  111. (when (looking-at "[ \t]")
  112. (skip-chars-forward " \t")
  113. (let ((here (point))
  114. (cur-status (ledger-state-from-char (char-after))))
  115. (skip-chars-forward "*! ")
  116. (let ((width (- (point) here)))
  117. (when (> width 0)
  118. (delete-region here (point))
  119. (save-excursion
  120. (if (search-forward " " (line-end-position) t)
  121. (insert (make-string width ? ))))))
  122. (let (inserted)
  123. (if cur-status
  124. (if (and style (eq style 'cleared))
  125. (progn
  126. (insert "* ")
  127. (setq inserted 'cleared)))
  128. (if (and style (eq style 'pending))
  129. (progn
  130. (insert "! ")
  131. (setq inserted 'pending))
  132. (progn
  133. (insert "* ")
  134. (setq inserted 'cleared))))
  135. (if (and inserted
  136. (re-search-forward "\\(\t\\| [ \t]\\)"
  137. (line-end-position) t))
  138. (cond
  139. ((looking-at "\t")
  140. (delete-char 1))
  141. ((looking-at " [ \t]")
  142. (delete-char 2))
  143. ((looking-at " ")
  144. (delete-char 1))))
  145. (setq new-status inserted))))
  146. (setq inhibit-modification-hooks nil))
  147. ;; This excursion cleans up the xact so that it displays
  148. ;; minimally. This means that if all posts are cleared, remove
  149. ;; the marks and clear the entire transaction.
  150. (save-excursion
  151. (goto-char (car bounds))
  152. (forward-line)
  153. (let ((first t)
  154. (state nil)
  155. (hetero nil))
  156. (while (and (not hetero) (looking-at "[ \t]"))
  157. (skip-chars-forward " \t")
  158. (let ((cur-status (ledger-state-from-char (char-after))))
  159. (if (not (eq cur-status 'comment))
  160. (if first
  161. (setq state cur-status
  162. first nil)
  163. (if (not (eq state cur-status))
  164. (setq hetero t)))))
  165. (forward-line))
  166. (when (and (not hetero) (not (eq state nil)))
  167. (goto-char (car bounds))
  168. (forward-line)
  169. (while (looking-at "[ \t]")
  170. (skip-chars-forward " \t")
  171. (let ((here (point)))
  172. (skip-chars-forward "*! ")
  173. (let ((width (- (point) here)))
  174. (when (> width 0)
  175. (delete-region here (point))
  176. (if (re-search-forward "\\(\t\\| [ \t]\\)"
  177. (line-end-position) t)
  178. (insert (make-string width ? ))))))
  179. (forward-line))
  180. (goto-char (car bounds))
  181. (skip-chars-forward "0-9./=\\-") ;; Skip the date
  182. (skip-chars-forward " \t") ;; Skip the white space
  183. (insert (ledger-char-from-state state) " ")
  184. (setq new-status state)
  185. (if (re-search-forward "\\(\t\\| [ \t]\\)"
  186. (line-end-position) t)
  187. (cond
  188. ((looking-at "\t")
  189. (delete-char 1))
  190. ((looking-at " [ \t]")
  191. (delete-char 2))
  192. ((looking-at " ")
  193. (delete-char 1)))))))
  194. new-status))
  195. (defun ledger-toggle-current (&optional style)
  196. "Toggle the current thing at point with optional STYLE."
  197. (interactive)
  198. (if (or ledger-clear-whole-transactions
  199. (eq 'transaction (ledger-thing-at-point)))
  200. (progn
  201. (save-excursion
  202. (forward-line)
  203. (goto-char (line-beginning-position))
  204. (while (and (not (eolp))
  205. (save-excursion
  206. (not (eq 'transaction (ledger-thing-at-point)))))
  207. (if (looking-at "\\s-+[*!]")
  208. (ledger-toggle-current-posting style))
  209. (forward-line)
  210. (goto-char (line-beginning-position))))
  211. (ledger-toggle-current-transaction style))
  212. (ledger-toggle-current-posting style)))
  213. (defun ledger-toggle-current-transaction (&optional style)
  214. "Toggle the transaction at point using optional STYLE."
  215. (interactive)
  216. (save-excursion
  217. (when (or (looking-at "^[0-9]")
  218. (re-search-backward "^[0-9]" nil t))
  219. (skip-chars-forward "0-9./=\\-")
  220. (delete-horizontal-space)
  221. (if (or (eq (ledger-state-from-char (char-after)) 'pending)
  222. (eq (ledger-state-from-char (char-after)) 'cleared))
  223. (progn
  224. (delete-char 1)
  225. (when (and style (eq style 'cleared))
  226. (insert " *")
  227. 'cleared))
  228. (if (and style (eq style 'pending))
  229. (progn
  230. (insert " ! ")
  231. 'pending)
  232. (progn
  233. (insert " * ")
  234. 'cleared))))))
  235. (provide 'ledger-state)
  236. ;;; ledger-state.el ends here