Set up fontification independent of font-lock.

Basic functionality in place.  need to test further and expand detail fortification.
This commit is contained in:
Craig Earls 2014-08-24 18:37:24 -07:00
parent ce31029236
commit 4deaeb02c9
6 changed files with 323 additions and 27 deletions

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

@ -0,0 +1,173 @@
;;; 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
(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))))
(forward-paragraph)
(forward-char))))
(defun ledger-fontify-activate ()
"add hook to fontify after buffer changes"
(interactive)
(if (string= (format-mode-line 'mode-name) "Ledger")
(progn
(add-hook 'post-command-hook 'ledger-fontify-buffer-part)
;; this is a silly work around to emacs bug 16796 wherein
;; after-change-functions is randomly reset to nil. Before
;; each change make sure after-change-functions is properly
;; set.
; (add-hook 'before-change-functions 'ledger-fontify-ensure-after-change-hook)
)))
;; (defun ledger-fontify-ensure-after-change-hook (beg end)
;; (if (string= (format-mode-line 'mode-name) "Ledger")
;; (add-hook 'after-change-functions 'ledger-fontify-buffer-part)))
(defun ledger-fontify-buffer-part ()
(save-excursion
(backward-paragraph)
(forward-char)
(cond ((looking-at ledger-xact-start-regex)
(ledger-fontify-xact-at (point)))
((looking-at ledger-directive-start-regex)
(ledger-fontify-directive-at (point))))))
(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 (extends)
"do line-by-line detailed fontification of xact"
(save-excursion
(ledger-fontify-xact-start (car extents))))
(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-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"

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)
@ -321,18 +313,6 @@ With a prefix argument, remove the effective date. "
(ledger-schedule-check-available) (ledger-schedule-check-available)
;;(ledger-post-setup) ;;(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)
(set (make-local-variable 'font-lock-defaults)
'(ledger-font-lock-keywords nil t)))
(setq font-lock-extend-region-functions
(list #'font-lock-extend-region-wholelines))
(setq font-lock-multiline nil)
(set (make-local-variable 'pcomplete-parse-arguments-function) (set (make-local-variable 'pcomplete-parse-arguments-function)
'ledger-parse-arguments) 'ledger-parse-arguments)
(set (make-local-variable 'pcomplete-command-completion-function) (set (make-local-variable 'pcomplete-command-completion-function)
@ -340,13 +320,17 @@ With a prefix argument, remove the effective date. "
(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)
(ledger-fontify-whole-buffer)
(ledger-fontify-activate)
;(add-hook 'after-save-hook) ;(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)) (set (make-local-variable 'indent-region-function) 'ledger-post-align-postings)
(run-mode-hooks))
(defun ledger-set-year (newyear) (defun ledger-set-year (newyear)

View file

@ -329,7 +329,27 @@
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
" ?\\([[:word:] ]+\\)" ;; desc, subexp 7
"\\(\n\\|;.*\\)" ;; comment, subexp 8
))
(defconst ledger-posting-regex
(concat "^[ \t]+" ;; initial white space
"\\("
"\\([[:word:]: ]*?\n?\\) " ;; account, subexpr 2
"\\(.*?\\)" ;; amount, subexpr 3
"\\(\n\\|\\(;.*\\)\\)" ;; 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)