Merge branch 'new-fontification-scheme'

This commit is contained in:
Craig Earls 2014-09-08 20:42:01 -07:00
commit 64c541a3d8
6 changed files with 400 additions and 54 deletions

201
lisp/ledger-fontify.el Normal file
View file

@ -0,0 +1,201 @@
;;; ledger-fontify.el --- Provide custom fontification for ledger-mode
;; Copyright (C) 2014 Craig P. Earls (enderw88 at gmail dot com)
;; This file is not part of GNU Emacs.
;; This is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 2, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
;; MA 02110-1301 USA.
;;; Commentary:
;; Font-lock-mode doesn't handle multiline syntax very well. This
;; code provides font lock that is sensitive to overall transaction
;; states
(provide 'ledger-fontify)
(defcustom ledger-fontify-xact-state-overrides t
"If t the overall xact state (cleard, pending, nil) will
control the font of the entire transaction, not just the payee
line."
:type 'boolean
:group 'ledger-fontification)
;; (defun ledger-fontify-whole-buffer ()
;; (interactive)
;; (save-excursion
;; (message "Ledger fontify whole buffer")
;; (goto-char (point-min))
;; (while (not (eobp))
;; (cond ((looking-at ledger-xact-start-regex)
;; (ledger-fontify-xact-at (point)))
;; ((looking-at ledger-directive-start-regex)
;; (ledger-fontify-directive-at (point))))
;; (ledger-xact-next-xact-or-directive) ;; gets to beginning of next xact
;; )))
;; (defun ledger-fontify-activate ()
;; "add hook to fontify after buffer changes"
;; (interactive)
;; (if (string= (format-mode-line 'mode-name) "Ledger")
;; (progn
;; (ledger-fontify-whole-buffer)
;; (add-hook 'after-change-functions 'ledger-fontify-buffer-part)
;; (add-hook 'before-change-functions 'ledger-fontify-ensure-activation)
;; (message "ledger-fontify-activate called"))))
;; (defun ledger-fontify-ensure-activation (beg end)
;; (if (string= (format-mode-line 'mode-name) "Ledger")
;; (add-hook 'after-change-functions 'ledger-fontify-buffer-part)))
(defun ledger-fontify-buffer-part (beg end len)
(save-excursion
(unless beg (setq beg (point-min)))
(unless end (setq end (point-max)))
(unless len (setq len (- end beg)))
(goto-char beg)
(while (< (point) end)
(cond ((or (looking-at ledger-xact-start-regex)
(looking-at ledger-posting-regex))
(ledger-fontify-xact-at (point)))
((looking-at ledger-directive-start-regex)
(ledger-fontify-directive-at (point))))
(ledger-xact-next-xact-or-directive))))
(defun ledger-fontify-xact-at (position)
(interactive "d")
(let ((extents (ledger-find-xact-extents position))
(state (ledger-transaction-state)))
(if (and ledger-fontify-xact-state-overrides state)
(cond ((eq state 'cleared)
(ledger-fontify-set-face extents 'ledger-font-xact-cleared-face))
((eq state 'pending)
(ledger-fontify-set-face extents 'ledger-font-xact-pending-face)))
(ledger-fontify-xact-by-line extents))))
(defun ledger-fontify-xact-by-line (extents)
"do line-by-line detailed fontification of xact"
(save-excursion
(ledger-fontify-xact-start (car extents))
(while (< (point) (cadr extents))
(ledger-fontify-posting (point))
(forward-line))))
(defun ledger-fontify-xact-start (pos)
(interactive "d")
(goto-char pos)
(let ((state nil))
(re-search-forward ledger-xact-start-regex)
(ledger-fontify-set-face (list (match-beginning 1) (match-end 1)) 'ledger-font-posting-date-face)
(save-match-data (setq state (ledger-state-from-string (s-trim (match-string 5)))))
(ledger-fontify-set-face (list (match-beginning 7) (match-end 7))
(cond ((eq state 'pending)
'ledger-font-payee-pending-face)
((eq state 'cleared)
'ledger-font-payee-cleared-face)
(t
'ledger-font-payee-uncleared-face)))
(ledger-fontify-set-face (list (match-beginning 8)
(match-end 8)) 'ledger-font-comment-face)))
(defun ledger-fontify-posting (pos)
(let ((state nil))
(re-search-forward ledger-posting-regex)
(if (match-string 1)
(save-match-data (setq state (ledger-state-from-string (s-trim (match-string 1))))))
(ledger-fontify-set-face (list (match-beginning 0) (match-end 2))
(cond ((eq state 'cleared)
'ledger-font-posting-account-cleared-face)
((eq state 'pending)
'ledger-font-posting-account-pending-face)
(t
'ledger-font-posting-account-face)))
(ledger-fontify-set-face (list (match-beginning 4) (match-end 4))
(cond ((eq state 'cleared)
'ledger-font-posting-account-cleared-face)
((eq state 'cleared)
'ledger-font-posting-account-pending-face)
(t
'ledger-font-posting-amount-face)))
(ledger-fontify-set-face (list (match-beginning 5) (match-end 5))
'ledger-font-comment-face)))
(defun ledger-fontify-directive-at (position)
(interactive "d")
(let ((extents (ledger-find-xact-extents position))
(face 'ledger-font-default-face))
(cond ((looking-at "=")
(setq face 'ledger-font-auto-xact-face))
((looking-at "~")
(setq face 'ledger-font-periodic-xact-face))
((looking-at "[;#%|\\*]")
(setq face 'ledger-font-comment-face))
((looking-at "\\(year\\)\\|Y")
(setq face 'ledger-font-year-directive-face))
((looking-at "account")
(setq face 'ledger-font-account-directive-face))
((looking-at "apply")
(setq face 'ledger-font-apply-directive-face))
((looking-at "alias")
(setq face 'ledger-font-alias-directive-face))
((looking-at "assert")
(setq face 'ledger-font-assert-directive-face))
((looking-at "\\(bucket\\)\\|A")
(setq face 'ledger-font-bucket-directive-face))
((looking-at "capture")
(setq face 'ledger-font-capture-directive-face))
((looking-at "check")
(setq face 'ledger-font-check-directive-face))
((looking-at "commodity")
(setq face 'ledger-font-commodity-directive-face))
((looking-at "define")
(setq face 'ledger-font-define-directive-face))
((looking-at "end")
(setq face 'ledger-font-end-directive-face))
((looking-at "expr")
(setq face 'ledger-font-expr-directive-face))
((looking-at "fixed")
(setq face 'ledger-font-fixed-directive-face))
((looking-at "include")
(setq face 'ledger-font-include-directive-face))
((looking-at "payee")
(setq face 'ledger-font-payee-directive-face))
((looking-at "tag")
(setq face 'ledger-font-tag-directive-face)))
(ledger-fontify-set-face extents face)))
(defun ledger-fontify-set-face (extents face)
(put-text-property (car extents) (cadr extents) 'face face))
(defun s-trim-left (s)
"Remove whitespace at the beginning of S."
(if (string-match "\\`[ \t\n\r]+" s)
(replace-match "" t t s)
s))
(defun s-trim-right (s)
"Remove whitespace at the end of S."
(if (string-match "[ \t\n\r]+\\'" s)
(replace-match "" t t s)
s))
(defun s-trim (s)
"Remove whitespace at the beginning and end of S."
(s-trim-left (s-trim-right s)))
;;; ledger-fontify.el ends here

View file

@ -29,6 +29,32 @@
(require 'ledger-regex) (require 'ledger-regex)
(defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger) (defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger)
(defface ledger-font-auto-xact-face
`((t :foreground "orange" :weight normal))
"Default face for automatic transactions"
:group 'ledger-faces)
(defface ledger-font-periodic-xact-face
`((t :foreground "green" :weight normal))
"Default face for automatic transactions"
:group 'ledger-faces)
(defface ledger-font-xact-cleared-face
`((t :foreground "#AAAAAA" :weight normal))
"Default face for cleared transaction"
:group 'ledger-faces)
(defface ledger-font-xact-pending-face
`((t :foreground "#444444" :weight normal))
"Default face for pending transaction"
:group 'ledger-faces)
(defface ledger-font-xact-open-face
`((t :foreground "#000000" :weight normal))
"Default face for transaction under point"
:group 'ledger-faces)
(defface ledger-font-payee-uncleared-face (defface ledger-font-payee-uncleared-face
`((t :foreground "#dc322f" :weight bold )) `((t :foreground "#dc322f" :weight bold ))
"Default face for Ledger" "Default face for Ledger"
@ -36,7 +62,7 @@
(defface ledger-font-payee-cleared-face (defface ledger-font-payee-cleared-face
`((t :inherit ledger-font-other-face)) `((t :inherit ledger-font-other-face))
"Default face for cleared (*) transactions" "Default face for cleared (*) payees"
:group 'ledger-faces) :group 'ledger-faces)
(defface ledger-font-xact-highlight-face (defface ledger-font-xact-highlight-face
@ -44,6 +70,7 @@
"Default face for transaction under point" "Default face for transaction under point"
:group 'ledger-faces) :group 'ledger-faces)
(defface ledger-font-pending-face (defface ledger-font-pending-face
`((t :foreground "#cb4b16" :weight normal )) `((t :foreground "#cb4b16" :weight normal ))
"Default face for pending (!) transactions" "Default face for pending (!) transactions"
@ -54,6 +81,91 @@
"Default face for other transactions" "Default face for other transactions"
:group 'ledger-faces) :group 'ledger-faces)
(defface ledger-font-directive-face
`((t :foreground "#009900" :weight normal))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-account-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-apply-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-alias-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-assert-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-bucket-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-capture-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-check-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-commodity-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-define-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-end-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-expr-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-fixed-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-include-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-payee-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-tag-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-year-directive-face
`((t :inherit ledger-font-directive-face))
"Default face for other transactions"
:group 'ledger-faces)
(defface ledger-font-posting-account-face (defface ledger-font-posting-account-face
`((t :foreground "#268bd2" )) `((t :foreground "#268bd2" ))
"Face for Ledger accounts" "Face for Ledger accounts"
@ -115,30 +227,25 @@
"Default face for pending (!) transactions in the reconcile window" "Default face for pending (!) transactions in the reconcile window"
:group 'ledger-faces) :group 'ledger-faces)
(defvar ledger-font-lock-keywords
`(("account" . ledger-font-account-directive-face)
("apply" . ledger-font-apply-directive-face)
("alias" . ledger-font-alias-directive-face)
("assert" . ledger-font-assert-directive-face)
("bucket" . ledger-font-bucket-directive-face)
("capture" . ledger-font-capture-directive-face)
("check" . ledger-font-check-directive-face)
("commodity" . ledger-font-commodity-directive-face)
("define" . ledger-font-define-directive-face)
("end" . ledger-font-end-directive-face)
("expr" . ledger-font-expr-directive-face)
("fixed" . ledger-font-fixed-directive-face)
("include" . ledger-font-include-directive-face)
("payee" . ledger-font-payee-directive-face)
("tag" . ledger-font-tag-directive-face)
("year" . ledger-font-year-directive-face))
"Expressions to highlight in Ledger mode.")
(defvar ledger-font-lock-keywords
`( ;; (,ledger-other-entries-regex 1
;; ledger-font-other-face)
(,ledger-comment-regex 0
'ledger-font-comment-face)
(,ledger-amount-regex 0
'ledger-font-posting-amount-face)
(,ledger-multiline-comment-regex 0 'ledger-font-comment-face)
(,ledger-payee-pending-regex 2
'ledger-font-payee-pending-face) ; Works
(,ledger-payee-cleared-regex 2
'ledger-font-payee-cleared-face) ; Works
(,ledger-payee-uncleared-regex 2
'ledger-font-payee-uncleared-face) ; Works
(,ledger-account-cleared-regex 2
'ledger-font-posting-account-cleared-face) ; Works
(,ledger-account-pending-regex 2
'ledger-font-posting-account-pending-face) ; Works
(,ledger-account-any-status-regex 2
'ledger-font-posting-account-face) ; Works
(,ledger-other-entries-regex 1
'ledger-font-other-face))
"Expressions to highlight in Ledger mode.")
(provide 'ledger-fonts) (provide 'ledger-fonts)

View file

@ -35,6 +35,7 @@
(require 'ledger-context) (require 'ledger-context)
(require 'ledger-exec) (require 'ledger-exec)
(require 'ledger-fonts) (require 'ledger-fonts)
(require 'ledger-fontify)
(require 'ledger-init) (require 'ledger-init)
(require 'ledger-occur) (require 'ledger-occur)
(require 'ledger-post) (require 'ledger-post)
@ -228,15 +229,6 @@ With a prefix argument, remove the effective date. "
(ledger-post-align-postings (point-min) (point-max)) (ledger-post-align-postings (point-min) (point-max))
(ledger-mode-remove-extra-lines)) (ledger-mode-remove-extra-lines))
(defvar ledger-mode-syntax-table
(let ((table (make-syntax-table)))
;; Support comments via the syntax table
(modify-syntax-entry ?\; "< b" table)
(modify-syntax-entry ?\n "> b" table)
table)
"Syntax table for `ledger-mode' buffers.")
(defvar ledger-mode-map (defvar ledger-mode-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map [(control ?c) (control ?a)] 'ledger-add-transaction) (define-key map [(control ?c) (control ?a)] 'ledger-add-transaction)
@ -315,38 +307,29 @@ With a prefix argument, remove the effective date. "
["Kill Report" ledger-report-kill ledger-works])) ["Kill Report" ledger-report-kill ledger-works]))
;;;###autoload ;;;###autoload
(define-derived-mode ledger-mode text-mode "Ledger" (define-derived-mode ledger-mode text-mode "Ledger"
"A mode for editing ledger data files." "A mode for editing ledger data files."
(ledger-check-version) (ledger-check-version)
(ledger-schedule-check-available) (ledger-schedule-check-available)
;;(ledger-post-setup)
(set-syntax-table ledger-mode-syntax-table)
(set (make-local-variable 'comment-start) "; ")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'indent-tabs-mode) nil)
(if (boundp 'font-lock-defaults) (if (boundp 'font-lock-defaults)
(set (make-local-variable 'font-lock-defaults) (setq-local font-lock-defaults
'(ledger-font-lock-keywords nil t))) '(ledger-font-lock-keywords t t nil nil
(setq font-lock-extend-region-functions (font-lock-fontify-region-function . ledger-fontify-buffer-part))))
(list #'font-lock-extend-region-wholelines))
(setq font-lock-multiline nil)
(set (make-local-variable 'pcomplete-parse-arguments-function) (setq-local pcomplete-parse-arguments-function 'ledger-parse-arguments)
'ledger-parse-arguments) (setq-local pcomplete-command-completion-function 'ledger-complete-at-point)
(set (make-local-variable 'pcomplete-command-completion-function)
'ledger-complete-at-point)
(add-hook 'completion-at-point-functions 'pcomplete-completions-at-point nil t) (add-hook 'completion-at-point-functions 'pcomplete-completions-at-point nil t)
(add-hook 'after-save-hook 'ledger-report-redo) (add-hook 'after-save-hook 'ledger-report-redo)
;(add-hook 'after-save-hook)
(add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t)
(add-hook 'before-revert-hook 'ledger-occur-remove-all-overlays nil t) (add-hook 'before-revert-hook 'ledger-occur-remove-all-overlays nil t)
(ledger-init-load-init-file) (ledger-init-load-init-file)
(set (make-local-variable 'indent-region-function) 'ledger-post-align-postings)) (setq-local indent-region-function 'ledger-post-align-postings))
(defun ledger-set-year (newyear) (defun ledger-set-year (newyear)

View file

@ -329,7 +329,29 @@
ledger-iso-date-regexp ledger-iso-date-regexp
"\\([ *!]+\\)" ;; mark "\\([ *!]+\\)" ;; mark
"\\((.*)\\)?" ;; code "\\((.*)\\)?" ;; code
"\\(.*\\)" ;; desc "\\([[:word:] ]+\\)" ;; desc
"\\)")) "\\)"))
(defconst ledger-xact-start-regex
(concat ledger-iso-date-regexp ;; subexp 1
" ?\\([ *!]\\)" ;; mark, subexp 5
" ?\\((.*)\\)?" ;; code, subexp 6
" ?\\([^;\n]+\\)" ;; desc, subexp 7
"\\(\n\\|;.*\\)" ;; comment, subexp 8
))
(defconst ledger-posting-regex
(concat "^[ \t]+ ?" ;; initial white space
"\\([*!]\\)? ?" ;; state, subexpr 1
"\\([[:word:]: ]+\\(\n\\|[ \t][ \t]\\)\\)" ;; account, subexpr 2
"\\([^;\n]*\\)" ;; amount, subexpr 4
"\\(.*\\)" ;; comment, subexpr 5
))
(defconst ledger-directive-start-regex
"[=~;#%|\\*[A-Za-z]")
(provide 'ledger-regex) (provide 'ledger-regex)

View file

@ -65,6 +65,14 @@
((eql state-char ?\;) 'comment) ((eql state-char ?\;) 'comment)
(t nil))) (t nil)))
(defun ledger-state-from-string (state-string)
"Get state from STATE-CHAR."
(cond ((string= state-string "!") 'pending)
((string= state-string "*") 'cleared)
((string= state-string ";") 'comment)
(t nil)))
(defun ledger-toggle-current-posting (&optional style) (defun ledger-toggle-current-posting (&optional style)
"Toggle the cleared status of the transaction under point. "Toggle the cleared status of the transaction under point.
Optional argument STYLE may be `pending' or `cleared', depending Optional argument STYLE may be `pending' or `cleared', depending

View file

@ -40,9 +40,8 @@
(make-variable-buffer-local 'ledger-xact-highlight-overlay) (make-variable-buffer-local 'ledger-xact-highlight-overlay)
(defun ledger-find-xact-extents (pos) (defun ledger-find-xact-extents (pos)
"Return point for beginning of xact and and of xact containing position. "Return list containing point for beginning and end of xact containing POS.
Requires empty line separating xacts. Argument POS is a location Requires empty line separating xacts."
within the transaction."
(interactive "d") (interactive "d")
(save-excursion (save-excursion
(goto-char pos) (goto-char pos)
@ -207,6 +206,32 @@ correct chronological place in the buffer."
(insert (car args) " \n\n") (insert (car args) " \n\n")
(end-of-line -1))))) (end-of-line -1)))))
(defun ledger-xact-start-xact-or-directive-p ()
"return t if at the beginning of an empty line or line
beginning with whitespace"
(not (looking-at "[ \t]\\|\\(^$\\)")))
(defun ledger-xact-next-xact-or-directive ()
"move to the beginning of the next xact"
(interactive)
(beginning-of-line)
(if (ledger-xact-start-xact-or-directive-p) ; if we are the start of an xact, move forward to the next xact
(progn
(forward-line)
(if (not (ledger-xact-start-xact-or-directive-p)) ; we have moved forward and are not at another xact, recurse forward
(ledger-xact-next-xact-or-directive)))
(while (not (or (eobp) ; we didn't start off at the beginning of an xact
(ledger-xact-start-xact-or-directive-p)))
(forward-line))))
(defun ledger-xact-next-xact ()
(interactive)
(beginning-of-line)
(if (looking-at ledger-xact-start-regex)
(forward-line))
(re-search-forward ledger-xact-start-regex)
(forward-line -1))
(provide 'ledger-xact) (provide 'ledger-xact)