From 05a1066ff0521af95b3761376c853d96312ca2f3 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 3 Aug 2006 07:14:39 +0000 Subject: [PATCH] (font-lock-beg, font-lock-end, font-lock-extend-region-functions): New vars. (font-lock-extend-region-multiline) (font-lock-extend-region-wholelines): New functions. (font-lock-default-fontify-region): Use them. (font-lock-extend-jit-lock-region-after-change): Only round up if font-lock-default-fontify-region will do it as well. --- etc/NEWS | 5 +++ lisp/ChangeLog | 8 ++++ lisp/font-lock.el | 94 ++++++++++++++++++++++++++++++++++------------ lispref/modes.texi | 6 ++- 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 52fcc1eea2a..3f986846f17 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -5363,6 +5363,11 @@ text to being a piece of code, so you'd put a `jit-lock-defer-multiline' property over the second half of the command to force (deferred) refontification of `bar' whenever the `e' is added/removed. +*** `font-lock-extend-region-functions' makes it possible to alter the way +the fontification region is chosen. This can be used to prevent rounding +up to whole lines, or to extend the region to include all related lines +of multiline constructs so that such constructs get properly recognized. + ** Major mode mechanism changes: +++ diff --git a/lisp/ChangeLog b/lisp/ChangeLog index dd1b63ba4ee..6329e0e699d 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,13 @@ 2006-08-03 Stefan Monnier + * font-lock.el (font-lock-beg, font-lock-end) + (font-lock-extend-region-functions): New vars. + (font-lock-extend-region-multiline) + (font-lock-extend-region-wholelines): New functions. + (font-lock-default-fontify-region): Use them. + (font-lock-extend-jit-lock-region-after-change): Only round up + if font-lock-default-fontify-region will do it as well. + * font-lock.el (font-lock-extend-after-change-region-function): Rename from font-lock-extend-region-function. (font-lock-extend-region): Remove by inlining at call sites. diff --git a/lisp/font-lock.el b/lisp/font-lock.el index a7cc1a09022..0cad924f201 100644 --- a/lisp/font-lock.el +++ b/lisp/font-lock.el @@ -1040,6 +1040,53 @@ The region it returns may start or end in the middle of a line.") Useful for things like RMAIL and Info where the whole buffer is not a very meaningful entity to highlight.") + +(defvar font-lock-beg) (defvar font-lock-end) +(defvar font-lock-extend-region-functions + '(font-lock-extend-region-wholelines + font-lock-extend-region-multiline) + "Special hook run just before proceeding to fontify a region. +This is used to allow major modes to help font-lock find safe buffer positions +as beginning and end of the fontified region. Its most common use is to solve +the problem of /identification/ of multiline elements by providing a function +that tries to find such elements and move the boundaries such that they do +not fall in the middle of one. +Each function is called with no argument; it is expected to adjust the +dynamically bound variables `font-lock-beg' and `font-lock-end'; and return +non-nil iff it did make such an adjustment. +These functions are run in turn repeatedly until they all return nil. +Put first the functions more likely to cause a change and cheaper to compute.") +;; Mark it as a special hook which doesn't use any global setting +;; (i.e. doesn't obey the element t in the buffer-local value). +(make-variable-buffer-local 'font-lock-extend-region-functions) + +(defun font-lock-extend-region-multiline () + "Move fontification boundaries away from any `font-lock-multiline' property." + (let ((changed nil)) + (when (and (> font-lock-beg (point-min)) + (get-text-property (1- font-lock-beg) 'font-lock-multiline)) + (setq changed t) + (setq font-lock-beg (or (previous-single-property-change + font-lock-beg 'font-lock-multiline) + (point-min)))) + ;; + (when (get-text-property font-lock-end 'font-lock-multiline) + (setq changed t) + (setq font-lock-end (or (text-property-any font-lock-end (point-max) + 'font-lock-multiline nil) + (point-max)))) + changed)) + + +(defun font-lock-extend-region-wholelines () + "Move fontification boundaries to beginning of lines." + (let ((changed nil)) + (goto-char font-lock-beg) + (unless (bobp) (setq changed t font-lock-beg (line-beginning-position))) + (goto-char font-lock-end) + (unless (bobp) (setq changed t font-lock-end (line-beginning-position 2))) + changed)) + (defun font-lock-default-fontify-region (beg end loudly) (save-buffer-state ((parse-sexp-lookup-properties @@ -1051,24 +1098,21 @@ a very meaningful entity to highlight.") ;; Use the fontification syntax table, if any. (when font-lock-syntax-table (set-syntax-table font-lock-syntax-table)) - (goto-char beg) - (setq beg (line-beginning-position)) - ;; check to see if we should expand the beg/end area for - ;; proper multiline matches - (when (and (> beg (point-min)) - (get-text-property (1- beg) 'font-lock-multiline)) - ;; We are just after or in a multiline match. - (setq beg (or (previous-single-property-change - beg 'font-lock-multiline) - (point-min))) - (goto-char beg) - (setq beg (line-beginning-position))) - (setq end (or (text-property-any end (point-max) - 'font-lock-multiline nil) - (point-max))) - (goto-char end) - ;; Round up to a whole line. - (unless (bolp) (setq end (line-beginning-position 2))) + ;; Extend the region to fontify so that it starts and ends at + ;; safe places. + (let ((funs font-lock-extend-region-functions) + (font-lock-beg beg) + (font-lock-end end)) + (while funs + (setq funs (if (or (not (funcall (car funs))) + (eq funs font-lock-extend-region-functions)) + (cdr funs) + ;; If there's been a change, we should go through + ;; the list again since this new position may + ;; warrant a different answer from one of the fun + ;; we've already seen. + font-lock-extend-region-functions))) + (setq beg font-lock-beg end font-lock-end)) ;; Now do the fontification. (font-lock-unfontify-region beg end) (when font-lock-syntactic-keywords @@ -1144,12 +1188,14 @@ what properties to clear before refontifying a region.") ;; Finally, pre-enlarge the region to a whole number of lines, to try ;; and predict what font-lock-default-fontify-region will do, so as to ;; avoid double-redisplay. - (goto-char beg) - (forward-line 0) - (setq jit-lock-start (min jit-lock-start (point))) - (goto-char end) - (forward-line 1) - (setq jit-lock-end (max jit-lock-end (point)))))) + (when (memq 'font-lock-extend-region-wholelines + font-lock-extend-region-functions) + (goto-char beg) + (forward-line 0) + (setq jit-lock-start (min jit-lock-start (point))) + (goto-char end) + (forward-line 1) + (setq jit-lock-end (max jit-lock-end (point))))))) (defun font-lock-fontify-block (&optional arg) "Fontify some lines the way `font-lock-fontify-buffer' would. diff --git a/lispref/modes.texi b/lispref/modes.texi index 7ca8764a0a8..38227633c6b 100644 --- a/lispref/modes.texi +++ b/lispref/modes.texi @@ -3044,7 +3044,7 @@ closely related, and often getting one of them to work will appear to make the other also work. However, for reliable results you must attend explicitly to both aspects. - There are two ways to ensure correct identification of multiline + There are three ways to ensure correct identification of multiline constructs: @itemize @@ -3055,6 +3055,10 @@ property on the construct when it is added to the buffer. Use @code{font-lock-fontify-region-function} hook to extend the scan so that the scanned text never starts or ends in the middle of a multiline construct. +@item +Add a function to @code{font-lock-extend-region-functions} that does +the \emph{identification} and extends the scan so that the scanned +text never starts or ends in the middle of a multiline construct. @end itemize There are three ways to do rehighlighting of multiline constructs: -- 2.39.2