;;; font-lock.el --- Electric font lock mode
;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-;; 2000, 2001, 2002, 2003, 2004 2005 Free Software Foundation, Inc.
+;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
;; Author: jwz, then rms, then sm
;; Maintainer: FSF
;; GNU Emacs 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)
+;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;;
;; (add-hook 'foo-mode-hook
;; (lambda ()
-;; (make-local-variable 'font-lock-defaults)
-;; (setq font-lock-defaults '(foo-font-lock-keywords t))))
+;; (set (make-local-variable 'font-lock-defaults)
+;; '(foo-font-lock-keywords t))))
;;; Adding Font Lock support for modes:
;;
;; and within `bar-mode' there could be:
;;
-;; (make-local-variable 'font-lock-defaults)
-;; (setq font-lock-defaults '(bar-font-lock-keywords nil t))
+;; (set (make-local-variable 'font-lock-defaults)
+;; '(bar-font-lock-keywords nil t))
\f
;; What is fontification for? You might say, "It's to make my code look nice."
;; I think it should be for adding information in the form of cues. These cues
(other :tag "always" t)
(integer :tag "size"))
:group 'font-lock)
-
-(defcustom font-lock-lines-before 0
- "*Number of lines before the changed text to include in refontification."
- :type 'integer
- :group 'font-lock
- :version "22.1")
\f
;; Originally these variable values were face names such as `bold' etc.
where MATCHER can be either the regexp to search for, or the function name to
call to make the search (called with one argument, the limit of the search;
-it should return non-nil, move point, and set `match-data' appropriately iff
+it should return non-nil, move point, and set `match-data' appropriately if
it succeeds; like `re-search-forward' would).
MATCHER regexps can be generated via the function `regexp-opt'.
This is normally set via `font-lock-add-keywords' and
`font-lock-remove-keywords'.")
+(put 'font-lock-keywords-alist 'risky-local-variable t)
(defvar font-lock-removed-keywords-alist nil
"Alist of `font-lock-keywords' elements to be removed for major modes.
;; contain the new keywords.
(font-lock-update-removed-keyword-alist mode keywords how))
(t
+ (when (and font-lock-mode
+ (not (or font-lock-keywords font-lock-defaults)))
+ ;; The major mode has not set any keywords, so when we enabled
+ ;; font-lock-mode it only enabled the font-core.el part, not the
+ ;; font-lock-mode-internal. Try again.
+ (font-lock-mode -1)
+ (set (make-local-variable 'font-lock-defaults) '(nil t))
+ (font-lock-mode 1))
;; Otherwise set or add the keywords now.
;; This is a no-op if it has been done already in this buffer
;; for the correct major mode.
(append keywords old)))))
;; If the keywords were compiled before, compile them again.
(if was-compiled
- (set (make-local-variable 'font-lock-keywords)
- (font-lock-compile-keywords font-lock-keywords t)))))))
+ (setq font-lock-keywords
+ (font-lock-compile-keywords font-lock-keywords)))))))
(defun font-lock-update-removed-keyword-alist (mode keywords how)
"Update `font-lock-removed-keywords-alist' when adding new KEYWORDS to MODE."
;; If the keywords were compiled before, compile them again.
(if was-compiled
- (set (make-local-variable 'font-lock-keywords)
- (font-lock-compile-keywords font-lock-keywords t)))))))
+ (setq font-lock-keywords
+ (font-lock-compile-keywords font-lock-keywords)))))))
\f
;;; Font Lock Support mode.
(set (make-local-variable 'font-lock-fontified) t)
;; Use jit-lock.
(jit-lock-register 'font-lock-fontify-region
- (not font-lock-keywords-only))))))
+ (not font-lock-keywords-only))
+ ;; Tell jit-lock how we extend the region to refontify.
+ (add-hook 'jit-lock-after-change-extend-region-functions
+ 'font-lock-extend-jit-lock-region-after-change
+ nil t)))))
(defun font-lock-turn-off-thing-lock ()
(cond ((and (boundp 'fast-lock-mode) fast-lock-mode)
;; directives correctly and cleanly. (It is the same problem as fontifying
;; multi-line strings and comments; regexps are not appropriate for the job.)
+(defvar font-lock-extend-after-change-region-function nil
+ "A function that determines the region to refontify after a change.
+
+This variable is either nil, or is a function that determines the
+region to refontify after a change.
+It is usually set by the major mode via `font-lock-defaults'.
+Font-lock calls this function after each buffer change.
+
+The function is given three parameters, the standard BEG, END, and OLD-LEN
+from `after-change-functions'. It should return either a cons of the beginning
+and end buffer positions \(in that order) of the region to refontify, or nil
+\(which directs the caller to fontify a default region).
+This function should preserve the match-data.
+The region it returns may start or end in the middle of a line.")
+
(defun font-lock-fontify-buffer ()
"Fontify the current buffer the way the function `font-lock-mode' would."
(interactive)
+ (font-lock-set-defaults)
(let ((font-lock-verbose (or font-lock-verbose (interactive-p))))
(funcall font-lock-fontify-buffer-function)))
(funcall font-lock-unfontify-buffer-function))
(defun font-lock-fontify-region (beg end &optional loudly)
+ (font-lock-set-defaults)
(funcall font-lock-fontify-region-function beg end loudly))
(defun font-lock-unfontify-region (beg end)
(with-temp-message
(when verbose
(format "Fontifying %s..." (buffer-name)))
- ;; Make sure we have the right `font-lock-keywords' etc.
- (unless font-lock-mode
- (font-lock-set-defaults))
;; Make sure we fontify etc. in the whole buffer.
(save-restriction
(widen)
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
+ ;; This use of font-lock-multiline property is unreliable but is just
+ ;; a handy heuristic: in case you don't have a function that does
+ ;; /identification/ of multiline elements, you may still occasionally
+ ;; discover them by accident (or you may /identify/ them but not in all
+ ;; cases), in which case the font-lock-multiline property can help make
+ ;; sure you will properly *re*identify them during refontification.
+ 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 if 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 (bolp)
+ (setq changed t font-lock-beg (line-beginning-position)))
+ (goto-char font-lock-end)
+ (unless (bolp)
+ (unless (eq font-lock-end
+ (setq font-lock-end (line-beginning-position 2)))
+ (setq changed t)))
+ changed))
+
(defun font-lock-default-fontify-region (beg end loudly)
(save-buffer-state
((parse-sexp-lookup-properties
;; 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 (- 1 font-lock-lines-before)))
- ;; 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
;; Called when any modification is made to buffer text.
(defun font-lock-after-change-function (beg end old-len)
- (let ((inhibit-point-motion-hooks t)
- (inhibit-quit t))
- (save-excursion
+ (save-excursion
+ (let ((inhibit-point-motion-hooks t)
+ (inhibit-quit t)
+ (region (if font-lock-extend-after-change-region-function
+ (funcall font-lock-extend-after-change-region-function
+ beg end old-len))))
(save-match-data
- ;; Rescan between start of lines enclosing the region.
- (font-lock-fontify-region
- (progn (goto-char beg) (forward-line 0) (point))
- (progn (goto-char end) (forward-line 1) (point)))))))
+ (if region
+ ;; Fontify the region the major mode has specified.
+ (setq beg (car region) end (cdr region))
+ ;; Fontify the whole lines which enclose the region.
+ ;; Actually, this is not needed because
+ ;; font-lock-default-fontify-region already rounds up to a whole
+ ;; number of lines.
+ ;; (setq beg (progn (goto-char beg) (line-beginning-position))
+ ;; end (progn (goto-char end) (line-beginning-position 2)))
+ (unless (eq end (point-max))
+ ;; Rounding up to a whole number of lines should include the
+ ;; line right after `end'. Typical case: the first char of
+ ;; the line was deleted. Or a \n was inserted in the middle
+ ;; of a line.
+ (setq end (1+ end))))
+ (font-lock-fontify-region beg end)))))
+
+(defvar jit-lock-start) (defvar jit-lock-end)
+(defun font-lock-extend-jit-lock-region-after-change (beg end old-len)
+ "Function meant for `jit-lock-after-change-extend-region-functions'.
+This function does 2 things:
+- extend the region so that it not only includes the part that was modified
+ but also the surrounding text whose highlighting may change as a consequence.
+- anticipate (part of) the region extension that will happen later in
+ `font-lock-default-fontify-region', in order to avoid the need for
+ double-redisplay in `jit-lock-fontify-now'."
+ (save-excursion
+ ;; First extend the region as font-lock-after-change-function would.
+ (let ((region (if font-lock-extend-after-change-region-function
+ (funcall font-lock-extend-after-change-region-function
+ beg end old-len))))
+ (if region
+ (setq beg (min jit-lock-start (car region))
+ end (max jit-lock-end (cdr region))))
+ ;; Then extend the region obeying font-lock-multiline properties,
+ ;; indicating which part of the buffer needs to be refontified.
+ ;; !!! This is the *main* user of font-lock-multiline property !!!
+ ;; font-lock-after-change-function could/should also do that, but it
+ ;; doesn't need to because font-lock-default-fontify-region does
+ ;; it anyway. Here OTOH we have no guarantee that
+ ;; font-lock-default-fontify-region will be executed on this region
+ ;; any time soon.
+ ;; Note: contrary to font-lock-default-fontify-region, we do not do
+ ;; any loop here because we are not looking for a safe spot: we just
+ ;; mark the text whose appearance may need to change as a result of
+ ;; the buffer modification.
+ (when (and (> beg (point-min))
+ (get-text-property (1- beg) 'font-lock-multiline))
+ (setq beg (or (previous-single-property-change
+ beg 'font-lock-multiline)
+ (point-min))))
+ (when (< end (point-max))
+ (setq end
+ (if (get-text-property end 'font-lock-multiline)
+ (or (text-property-any end (point-max)
+ 'font-lock-multiline nil)
+ (point-max))
+ ;; Rounding up to a whole number of lines should include the
+ ;; line right after `end'. Typical case: the first char of
+ ;; the line was deleted. Or a \n was inserted in the middle
+ ;; of a line.
+ (1+ end))))
+ ;; Finally, pre-enlarge the region to a whole number of lines, to try
+ ;; and anticipate what font-lock-default-fontify-region will do, so as to
+ ;; avoid double-redisplay.
+ ;; We could just run `font-lock-extend-region-functions', but since
+ ;; the only purpose is to avoid the double-redisplay, we prefer to
+ ;; do here only the part that is cheap and most likely to be useful.
+ (when (memq 'font-lock-extend-region-wholelines
+ font-lock-extend-region-functions)
+ (goto-char beg)
+ (setq jit-lock-start (min jit-lock-start (line-beginning-position)))
+ (goto-char end)
+ (setq jit-lock-end
+ (max jit-lock-end
+ (if (bolp) (point) (line-beginning-position 2))))))))
(defun font-lock-fontify-block (&optional arg)
"Fontify some lines the way `font-lock-fontify-buffer' would.
;; If `font-lock-syntactic-keywords' is not compiled, compile it.
(unless (eq (car font-lock-syntactic-keywords) t)
(setq font-lock-syntactic-keywords (font-lock-compile-keywords
- font-lock-syntactic-keywords)))
+ font-lock-syntactic-keywords
+ t)))
;; Get down to business.
(let ((case-fold-search font-lock-keywords-case-fold-search)
(keywords (cddr font-lock-syntactic-keywords))
LOUDLY, if non-nil, allows progress-meter bar."
(unless (eq (car font-lock-keywords) t)
(setq font-lock-keywords
- (font-lock-compile-keywords font-lock-keywords t)))
+ (font-lock-compile-keywords font-lock-keywords)))
(let ((case-fold-search font-lock-keywords-case-fold-search)
(keywords (cddr font-lock-keywords))
(bufname (buffer-name)) (count 0)
\f
;; Various functions.
-(defun font-lock-compile-keywords (keywords &optional regexp)
+(defun font-lock-compile-keywords (keywords &optional syntactic-keywords)
"Compile KEYWORDS into the form (t KEYWORDS COMPILED...)
Here each COMPILED is of the form (MATCHER HIGHLIGHT ...) as shown in the
`font-lock-keywords' doc string.
-If REGEXP is non-nil, it means these keywords are used for
-`font-lock-keywords' rather than for `font-lock-syntactic-keywords'."
+If SYNTACTIC-KEYWORDS is non-nil, it means these keywords are used for
+`font-lock-syntactic-keywords' rather than for `font-lock-keywords'."
+ (if (not font-lock-set-defaults)
+ ;; This should never happen. But some external packages sometimes
+ ;; call font-lock in unexpected and incorrect ways. It's important to
+ ;; stop processing at this point, otherwise we may end up changing the
+ ;; global value of font-lock-keywords and break highlighting in many
+ ;; other buffers.
+ (error "Font-lock trying to use keywords before setting them up"))
(if (eq (car-safe keywords) t)
keywords
(setq keywords
(cons t (cons keywords
(mapcar 'font-lock-compile-keyword keywords))))
- (if (and regexp
- (eq (or syntax-begin-function
- font-lock-beginning-of-syntax-function)
- 'beginning-of-defun)
+ (if (and (not syntactic-keywords)
+ (let ((beg-function
+ (or font-lock-beginning-of-syntax-function
+ syntax-begin-function)))
+ (or (eq beg-function 'beginning-of-defun)
+ (get beg-function 'font-lock-syntax-paren-check)))
(not beginning-of-defun-function))
;; Try to detect when a string or comment contains something that
;; looks like a defun and would thus confuse font-lock.
(cond ((not (and (listp keywords) (symbolp (car keywords))))
keywords)
((numberp level)
- (or (nth level keywords) (car (reverse keywords))))
+ (or (nth level keywords) (car (last keywords))))
((eq level t)
- (car (reverse keywords)))
+ (car (last keywords)))
(t
(car keywords))))
"Set fontification defaults appropriately for this mode.
Sets various variables using `font-lock-defaults' (or, if nil, using
`font-lock-defaults-alist') and `font-lock-maximum-decoration'."
- ;; Set fontification defaults iff not previously set for correct major mode.
+ ;; Set fontification defaults if not previously set for correct major mode.
(unless (and font-lock-set-defaults
(eq font-lock-mode-major-mode major-mode))
(setq font-lock-mode-major-mode major-mode)
(font-lock-remove-keywords nil removed-keywords))
;; Now compile the keywords.
(unless (eq (car font-lock-keywords) t)
- (set (make-local-variable 'font-lock-keywords)
- (font-lock-compile-keywords font-lock-keywords t))))))
+ (setq font-lock-keywords
+ (font-lock-compile-keywords font-lock-keywords))))))
\f
;;; Colour etc. support.
(goto-char (or (scan-sexps (point) 1) (point-max))))
(goto-char (match-end 2)))
(error t)))))
+
+;; C preprocessor(cpp) is used outside of C, C++ and Objective-C source file.
+;; e.g. assembler code and GNU linker script in Linux kernel.
+;; `cpp-font-lock-keywords' is handy for modes for the files.
+;;
+;; Here we cannot use `regexp-opt' because because regex-opt is not preloaded
+;; while font-lock.el is preloaded to emacs. So values pre-calculated with
+;; regexp-opt are used here.
+
+;; `cpp-font-lock-keywords-source-directives' is calculated from:
+;;
+;; (regexp-opt
+;; '("define" "elif" "else" "endif" "error" "file" "if" "ifdef"
+;; "ifndef" "import" "include" "line" "pragma" "undef" "warning"))
+;;
+(defconst cpp-font-lock-keywords-source-directives
+ "define\\|e\\(?:l\\(?:if\\|se\\)\\|ndif\\|rror\\)\\|file\\|i\\(?:f\\(?:n?def\\)?\\|mport\\|nclude\\)\\|line\\|pragma\\|undef\\|warning"
+ "Regular expressoin used in `cpp-font-lock-keywords'.")
+
+;; `cpp-font-lock-keywords-source-depth' is calculated from:
+;;
+;; (regexp-opt-depth (regexp-opt
+;; '("define" "elif" "else" "endif" "error" "file" "if" "ifdef"
+;; "ifndef" "import" "include" "line" "pragma" "undef" "warning")))
+;;
+(defconst cpp-font-lock-keywords-source-depth 0
+ "An integer representing regular expression depth of `cpp-font-lock-keywords-source-directives'.
+Used in `cpp-font-lock-keywords'.")
+
+(defconst cpp-font-lock-keywords
+ (let* ((directives cpp-font-lock-keywords-source-directives)
+ (directives-depth cpp-font-lock-keywords-source-depth))
+ (list
+ ;;
+ ;; Fontify error directives.
+ '("^#[ \t]*\\(?:error\\|warning\\)[ \t]+\\(.+\\)" 1 font-lock-warning-face prepend)
+ ;;
+ ;; Fontify filenames in #include <...> preprocessor directives as strings.
+ '("^#[ \t]*\\(?:import\\|include\\)[ \t]*\\(<[^>\"\n]*>?\\)"
+ 1 font-lock-string-face prepend)
+ ;;
+ ;; Fontify function macro names.
+ '("^#[ \t]*define[ \t]+\\([[:alpha:]_][[:alnum:]_$]*\\)("
+ (1 font-lock-function-name-face prepend)
+ ;;
+ ;; Macro arguments.
+ ((lambda (limit)
+ (re-search-forward
+ "\\(?:\\([[:alpha:]_][[:alnum:]_]*\\)[,]?\\)"
+ (or (save-excursion (re-search-forward ")" limit t))
+ limit)
+ t))
+ nil nil (1 font-lock-variable-name-face prepend)))
+ ;;
+ ;; Fontify symbol names in #elif or #if ... defined preprocessor directives.
+ '("^#[ \t]*\\(?:elif\\|if\\)\\>"
+ ("\\<\\(defined\\)\\>[ \t]*(?\\([[:alpha:]_][[:alnum:]_]*\\)?" nil nil
+ (1 font-lock-builtin-face prepend) (2 font-lock-variable-name-face prepend t)))
+ ;;
+ ;; Fontify otherwise as symbol names, and the preprocessor directive names.
+ (list
+ (concat "^\\(#[ \t]*\\(?:" directives
+ "\\)\\)\\>[ \t!]*\\([[:alpha:]_][[:alnum:]_]*\\)?")
+ '(1 font-lock-preprocessor-face prepend)
+ (list (+ 2 directives-depth)
+ 'font-lock-variable-name-face nil t))))
+ "Font lock keyords for C preprocessor directives.
+`c-mode', `c++-mode' and `objc-mode' have their own
+font lock keyords for C preprocessor directives. This definition is for the
+other modes in which C preprocessor directives are used. e.g. `asm-mode' and
+`ld-script-mode'.")
+
\f
;; Lisp.
`(;; Definitions.
(,(concat "(\\(def\\("
;; Function declarations.
- "\\(advice\\|varalias\\|alias\\|generic\\|macro\\*?\\|method\\|"
+ "\\(advice\\|alias\\|generic\\|macro\\*?\\|method\\|"
"setf\\|subst\\*?\\|un\\*?\\|"
"ine-\\(condition\\|"
- "\\(?:derived\\|\\(?:global-\\)?minor\\|generic\\)-mode\\|"
+ "\\(?:derived\\|\\(?:global\\(?:ized\\)?-\\)?minor\\|generic\\)-mode\\|"
"method-combination\\|setf-expander\\|skeleton\\|widget\\|"
"function\\|\\(compiler\\|modify\\|symbol\\)-macro\\)\\)\\|"
;; Variable declarations.
- "\\(const\\(ant\\)?\\|custom\\|face\\|parameter\\|var\\)\\|"
+ "\\(const\\(ant\\)?\\|custom\\|varalias\\|face\\|parameter\\|var\\)\\|"
;; Structure declarations.
"\\(class\\|group\\|theme\\|package\\|struct\\|type\\)"
"\\)\\)\\>"
"condition-case" "track-mouse"
"eval-after-load" "eval-and-compile" "eval-when-compile"
"eval-when" "eval-at-startup" "eval-next-after-load"
- "with-category-table"
+ "with-case-table" "with-category-table"
"with-current-buffer" "with-electric-help"
"with-local-quit" "with-no-warnings"
"with-output-to-string" "with-output-to-temp-buffer"
;; Erroneous structures.
("(\\(abort\\|assert\\|warn\\|check-type\\|cerror\\|error\\|signal\\)\\>" 1 font-lock-warning-face)
;; Words inside \\[] tend to be for `substitute-command-keys'.
- ("\\\\\\\\\\[\\(\\sw+\\)]" 1 font-lock-constant-face prepend)
+ ("\\\\\\\\\\[\\(\\sw+\\)\\]" 1 font-lock-constant-face prepend)
;; Words inside `' tend to be symbol names.
("`\\(\\sw\\sw+\\)'" 1 font-lock-constant-face prepend)
;; Constant values.
("\\<:\\sw+\\>" 0 font-lock-builtin-face)
;; ELisp and CLisp `&' keywords as types.
- ("\\&\\sw+\\>" . font-lock-type-face)
+ ("\\<\\&\\sw+\\>" . font-lock-type-face)
;; ELisp regexp grouping constructs
((lambda (bound)
(catch 'found