diff lisp/font-lock.el @ 460:223736d75acb r21-2-45

Import from CVS: tag r21-2-45
author cvs
date Mon, 13 Aug 2007 11:43:24 +0200
parents 3078fd1074e8
children 0784d089fdc9
line wrap: on
line diff
--- a/lisp/font-lock.el	Mon Aug 13 11:42:27 2007 +0200
+++ b/lisp/font-lock.el	Mon Aug 13 11:43:24 2007 +0200
@@ -313,6 +313,12 @@
 				      (integer :tag "size")))))
   :group 'font-lock)
 
+;;;###autoload
+(defcustom font-lock-fontify-string-delimiters nil
+  "*If non-nil, apply font-lock-string-face to string delimiters as well as
+string text when fontifying."
+  :type 'boolean
+  :group 'font-lock)
 
 ;; Fontification variables:
 
@@ -438,6 +444,45 @@
 ;;;###autoload
 (make-variable-buffer-local 'font-lock-keywords)
 
+;;;###autoload
+(defvar font-lock-syntactic-keywords nil
+  "A list of the syntactic keywords to highlight.
+Can be the list or the name of a function or variable whose value is the list.
+See `font-lock-keywords' for a description of the form of this list;
+the differences are listed below.  MATCH-HIGHLIGHT should be of the form:
+
+ (MATCH SYNTAX OVERRIDE LAXMATCH)
+
+where SYNTAX can be of the form (SYNTAX-CODE . MATCHING-CHAR), the name of a
+syntax table, or an expression whose value is such a form or a syntax table.
+OVERRIDE cannot be `prepend' or `append'.
+
+For example, an element of the form highlights syntactically:
+
+ (\"\\\\$\\\\(#\\\\)\" 1 (1 . nil))
+
+ a hash character when following a dollar character, with a SYNTAX-CODE of
+ 1 (meaning punctuation syntax).  Assuming that the buffer syntax table does
+ specify hash characters to have comment start syntax, the element will only
+ highlight hash characters that do not follow dollar characters as comments
+ syntactically.
+
+ (\"\\\\('\\\\).\\\\('\\\\)\"
+  (1 (7 . ?'))
+  (2 (7 . ?')))
+
+ both single quotes which surround a single character, with a SYNTAX-CODE of
+ 7 (meaning string quote syntax) and a MATCHING-CHAR of a single quote (meaning
+ a single quote matches a single quote).  Assuming that the buffer syntax table
+ does not specify single quotes to have quote syntax, the element will only
+ highlight single quotes of the form 'c' as strings syntactically.
+ Other forms, such as foo'bar or 'fubar', will not be highlighted as strings.
+
+This is normally set via `font-lock-defaults'."
+)
+;;;###autoload
+(make-variable-buffer-local 'font-lock-syntactic-keywords)
+
 (defvar font-lock-defaults nil
   "The defaults font Font Lock mode for the current buffer.
 Normally, do not set this directly.  If you are writing a major mode,
@@ -511,15 +556,15 @@
 This is normally set via `font-lock-defaults'.")
 (make-variable-buffer-local 'font-lock-syntax-table)
 
-;; These are used in the FSF version in syntactic font-locking.
-;; We do this all in C.
-;;; These record the parse state at a particular position, always the
-;;; start of a line.  Used to make
-;;; `font-lock-fontify-syntactically-region' faster.
-;(defvar font-lock-cache-position nil)
-;(defvar font-lock-cache-state nil)
-;(make-variable-buffer-local 'font-lock-cache-position)
-;(make-variable-buffer-local 'font-lock-cache-state)
+;; These record the parse state at a particular position, always the start of a
+;; line.  Used to make `font-lock-fontify-syntactically-region' faster.
+;; Previously, `font-lock-cache-position' was just a buffer position.  However,
+;; under certain situations, this occasionally resulted in mis-fontification.
+;; I think the "situations" were deletion with Lazy Lock mode's deferral.  sm.
+(defvar font-lock-cache-state nil)
+(defvar font-lock-cache-position nil)
+(make-variable-buffer-local 'font-lock-cache-state)
+(make-variable-buffer-local 'font-lock-cache-position)
 
 ;; If this is nil, we only use the beginning of the buffer if we can't use
 ;; `font-lock-cache-position' and `font-lock-cache-state'.
@@ -944,7 +989,14 @@
 (defsubst font-lock-remove-face (start end)
   ;; Remove any syntax highlighting on the characters in the range.
   (put-nonduplicable-text-property start end 'face nil)
-  (put-nonduplicable-text-property start end 'font-lock nil))
+  (put-nonduplicable-text-property start end 'font-lock nil)
+  (if lookup-syntax-properties
+      (put-nonduplicable-text-property start end 'syntax-table nil)))
+
+(defsubst font-lock-set-syntax (start end syntax)
+  ;; Set the face on the characters in the range.
+  (put-nonduplicable-text-property start end 'syntax-table syntax)
+  (put-nonduplicable-text-property start end 'font-lock t))
 
 (defsubst font-lock-any-faces-p (start end)
   ;; Return non-nil if we've put any syntax highlighting on
@@ -1084,8 +1136,10 @@
 	  ;; Use the fontification syntax table, if any.
 	  (if font-lock-syntax-table (set-syntax-table font-lock-syntax-table))
 	  ;; Now do the fontification.
-	  (if font-lock-keywords-only
-	      (font-lock-unfontify-region beg end)
+	  (font-lock-unfontify-region beg end)
+	  (when font-lock-syntactic-keywords
+	    (font-lock-fontify-syntactic-keywords-region beg end))
+	  (unless font-lock-keywords-only
 	    (font-lock-fontify-syntactically-region beg end loudly))
 	  (font-lock-fontify-keywords-region beg end loudly))
       ;; Clean up.
@@ -1222,118 +1276,6 @@
 
 ;; Syntactic fontification functions.
 
-;; Note: Here is the FSF version.  Our version is much faster because
-;; of the C support we provide.  This may be useful for reference,
-;; however, and perhaps there is something useful here that should
-;; be merged into our version.
-;;
-;(defun font-lock-fontify-syntactically-region (start end &optional loudly)
-;  "Put proper face on each string and comment between START and END.
-;START should be at the beginning of a line."
-;  (let ((synstart (if comment-start-skip
-;		       (concat "\\s\"\\|" comment-start-skip)
-;		     "\\s\""))
-;	 (comstart (if comment-start-skip
-;		       (concat "\\s<\\|" comment-start-skip)
-;		     "\\s<"))
-;	 state prev prevstate)
-;    (if loudly (message "Fontifying %s... (syntactically...)" (buffer-name)))
-;    (save-restriction
-;      (widen)
-;      (goto-char start)
-;      ;;
-;      ;; Find the state at the `beginning-of-line' before `start'.
-;      (if (eq start font-lock-cache-position)
-;	   ;; Use the cache for the state of `start'.
-;	   (setq state font-lock-cache-state)
-;	 ;; Find the state of `start'.
-;	 (if (null font-lock-beginning-of-syntax-function)
-;	     ;; Use the state at the previous cache position, if any, or
-;	     ;; otherwise calculate from `point-min'.
-;	     (if (or (null font-lock-cache-position)
-;		     (< start font-lock-cache-position))
-;		 (setq state (parse-partial-sexp (point-min) start))
-;	       (setq state (parse-partial-sexp font-lock-cache-position start
-;					       nil nil font-lock-cache-state)))
-;	   ;; Call the function to move outside any syntactic block.
-;	   (funcall font-lock-beginning-of-syntax-function)
-;	   (setq state (parse-partial-sexp (point) start)))
-;	 ;; Cache the state and position of `start'.
-;	 (setq font-lock-cache-state state
-;	       font-lock-cache-position start))
-;      ;;
-;      ;; If the region starts inside a string, show the extent of it.
-;      (if (nth 3 state)
-;	   (let ((beg (point)))
-;	     (while (and (re-search-forward "\\s\"" end 'move)
-;			 (nth 3 (parse-partial-sexp beg (point)
-;						    nil nil state))))
-;	     (put-text-property beg (point) 'face font-lock-string-face)
-;	     (setq state (parse-partial-sexp beg (point) nil nil state))))
-;      ;;
-;      ;; Likewise for a comment.
-;      (if (or (nth 4 state) (nth 7 state))
-;	   (let ((beg (point)))
-;	     (save-restriction
-;	       (narrow-to-region (point-min) end)
-;	       (condition-case nil
-;		   (progn
-;		     (re-search-backward comstart (point-min) 'move)
-;		     (forward-comment 1)
-;		     ;; forward-comment skips all whitespace,
-;		     ;; so go back to the real end of the comment.
-;		     (skip-chars-backward " \t"))
-;		 (error (goto-char end))))
-;	     (put-text-property beg (point) 'face font-lock-comment-face)
-;	     (setq state (parse-partial-sexp beg (point) nil nil state))))
-;      ;;
-;      ;; Find each interesting place between here and `end'.
-;      (while (and (< (point) end)
-;		   (setq prev (point) prevstate state)
-;		   (re-search-forward synstart end t)
-;		   (progn
-;		     ;; Clear out the fonts of what we skip over.
-;		     (remove-text-properties prev (point) '(face nil))
-;		     ;; Verify the state at that place
-;		     ;; so we don't get fooled by \" or \;.
-;		     (setq state (parse-partial-sexp prev (point)
-;						     nil nil state))))
-;	 (let ((here (point)))
-;	   (if (or (nth 4 state) (nth 7 state))
-;	       ;;
-;	       ;; We found a real comment start.
-;	       (let ((beg (match-beginning 0)))
-;		 (goto-char beg)
-;		 (save-restriction
-;		   (narrow-to-region (point-min) end)
-;		   (condition-case nil
-;		       (progn
-;			 (forward-comment 1)
-;			 ;; forward-comment skips all whitespace,
-;			 ;; so go back to the real end of the comment.
-;			 (skip-chars-backward " \t"))
-;		     (error (goto-char end))))
-;		 (put-text-property beg (point) 'face
-;				    font-lock-comment-face)
-;		 (setq state (parse-partial-sexp here (point) nil nil state)))
-;	     (if (nth 3 state)
-;		 ;;
-;		 ;; We found a real string start.
-;		 (let ((beg (match-beginning 0)))
-;		   (while (and (re-search-forward "\\s\"" end 'move)
-;			       (nth 3 (parse-partial-sexp here (point)
-;							  nil nil state))))
-;		   (put-text-property beg (point) 'face font-lock-string-face)
-;		   (setq state (parse-partial-sexp here (point)
-;						   nil nil state))))))
-;	 ;;
-;	 ;; Make sure `prev' is non-nil after the loop
-;	 ;; only if it was set on the very last iteration.
-;	 (setq prev nil)))
-;    ;;
-;    ;; Clean up.
-;    (and prev (remove-text-properties prev end '(face nil)))))
-
 (defun font-lock-lisp-like (mode)
   ;; Note: (or (get mode 'font-lock-lisp-like) (string-match ...)) is
   ;; not enough because the property needs to be able to specify a nil
@@ -1344,52 +1286,77 @@
     ;; in add-log, but I think this encompasses more modes.
     (string-match "lisp\\|scheme" (symbol-name mode))))
 
+;; fontify-syntactically-region used to use syntactically-sectionize, which
+;; was supposedly much faster than the FSF version because it was written in
+;; C. However, the FSF version uses parse-partial-sexp, which is also
+;; written in C, and the benchmarking I did showed the
+;; syntactically-sectionize code to be slower overall. So here's the FSF
+;; version, modified to support font-lock-doc-string-face.
+;; -- mct 2000-12-29
 (defun font-lock-fontify-syntactically-region (start end &optional loudly)
   "Put proper face on each string and comment between START and END.
 START should be at the beginning of a line."
   (if font-lock-keywords-only
       nil
+
+    ;; #### Shouldn't this just be using 'loudly??
     (when (and font-lock-verbose
 	       (>= (- end start) font-lock-message-threshold))
       (progress-feedback-with-label 'font-lock
 				    "Fontifying %s... (syntactically)" 5
 				    (buffer-name)))
-    (font-lock-unfontify-region start end loudly)
     (goto-char start)
-    (if (> end (point-max)) (setq end (point-max)))
-    (let ((lisp-like (font-lock-lisp-like major-mode)))
-      (syntactically-sectionize
-       #'(lambda (s e context depth)
-	   (let (face)
-	     (cond ((eq context 'string)
-		    (setq face
-			  ;; #### It would be nice if we handled
-			  ;; Python and other non-Lisp languages with
-			  ;; docstrings correctly.
-			  (if (and lisp-like (= depth 1))
-			      ;; really we should only use this if
-			      ;;  in position 3 depth 1, but that's
-			      ;;  too expensive to compute.
-			      'font-lock-doc-string-face
-			    'font-lock-string-face)))
-		   ((or (eq context 'comment)
-			(eq context 'block-comment))
-		    (setq face 'font-lock-comment-face)
-;		 ;; Don't fontify whitespace at the beginning of lines;
-;		 ;;  otherwise comment blocks may not line up with code.
-;		 ;; (This is sometimes a good idea, sometimes not; in any
-;		 ;; event it should be in C for speed --jwz)
-;		 (save-excursion
-;		     (goto-char s)
-;		     (while (prog1 (search-forward "\n" (1- e) 'move)
-;			      (setq face 'font-lock-comment-face)
-;			      (setq e (point)))
-;		       (skip-chars-forward " \t\n")
-;		       (setq s (point)))
-		   ))
-	     (font-lock-set-face s e face)))
-       start end)
-      )))
+
+    (let ((lisp-like (font-lock-lisp-like major-mode))
+	  (cache (marker-position font-lock-cache-position))
+	  state string beg depth)
+      ;;
+      ;; Find the state at the `beginning-of-line' before `start'.
+      (if (eq start cache)
+	  ;; Use the cache for the state of `start'.
+	  (setq state font-lock-cache-state)
+	;; Find the state of `start'.
+	(if (null font-lock-beginning-of-syntax-function)
+	    ;; Use the state at the previous cache position, if any, or
+	    ;; otherwise calculate from `point-min'.
+	    (if (or (null cache) (< start cache))
+		(setq state (parse-partial-sexp (point-min) start))
+	      (setq state (parse-partial-sexp cache start nil nil
+					      font-lock-cache-state)))
+	  ;; Call the function to move outside any syntactic block.
+	  (funcall font-lock-beginning-of-syntax-function)
+	  (setq state (parse-partial-sexp (point) start)))
+	;; Cache the state and position of `start'.
+	(setq font-lock-cache-state state)
+	(set-marker font-lock-cache-position start))
+      ;;
+      ;; If the region starts inside a string or comment, show the extent of it.
+      (when (or (nth 3 state) (nth 4 state))
+	(setq string (nth 3 state) beg (point))
+	(setq state (parse-partial-sexp (point) end nil nil state 'syntax-table))
+	(font-lock-set-face beg (point) (if string 
+					    font-lock-string-face
+					  font-lock-comment-face)))
+      ;;
+      ;; Find each interesting place between here and `end'.
+      (while (and (< (point) end)
+		  (progn
+		    (setq state (parse-partial-sexp (point) end nil nil state
+						    'syntax-table))
+		    (or (nth 3 state) (nth 4 state))))
+	(setq depth (nth 0 state) string (nth 3 state) beg (nth 8 state))
+	(setq state (parse-partial-sexp (point) end nil nil state 'syntax-table))
+	(if string
+	    ;; #### It would be nice if we handled Python and other
+	    ;; non-Lisp languages with docstrings correctly.
+	    (let ((face (if (and lisp-like (= depth 1))
+			    'font-lock-doc-string-face
+			  'font-lock-string-face)))
+	      (if font-lock-fontify-string-delimiters
+		  (font-lock-set-face beg (point) face)
+		(font-lock-set-face (+ beg 1) (- (point) 1) face)))
+	  (font-lock-set-face beg (point)
+			      font-lock-comment-face))))))
 
 ;;; Additional text property functions.
 
@@ -1473,6 +1440,101 @@
        object)
       (setq start next))))
 
+;;; Syntactic regexp fontification functions (taken from FSF Emacs 20.7.1)
+
+;; These syntactic keyword pass functions are identical to those keyword pass
+;; functions below, with the following exceptions; (a) they operate on
+;; `font-lock-syntactic-keywords' of course, (b) they are all `defun' as speed
+;; is less of an issue, (c) eval of property value does not occur JIT as speed
+;; is less of an issue, (d) OVERRIDE cannot be `prepend' or `append' as it
+;; makes no sense for `syntax-table' property values, (e) they do not do it
+;; LOUDLY as it is not likely to be intensive.
+
+(defun font-lock-apply-syntactic-highlight (highlight)
+  "Apply HIGHLIGHT following a match.
+ HIGHLIGHT should be of the form MATCH-HIGHLIGHT,
+ see `font-lock-syntactic-keywords'."
+  (let* ((match (nth 0 highlight))
+ 	 (start (match-beginning match)) (end (match-end match))
+ 	 (value (nth 1 highlight))
+ 	 (override (nth 2 highlight)))
+    (unless (numberp (car-safe value))
+      (setq value (eval value)))
+    (cond ((not start)
+ 	   ;; No match but we might not signal an error.
+ 	   (or (nth 3 highlight)
+ 	       (error "No match %d in highlight %S" match highlight)))
+ 	  ((not override)
+ 	   ;; Cannot override existing fontification.
+ 	   (or (map-extents 'extent-property (current-buffer)
+			    start end 'syntax-table)
+	       (font-lock-set-syntax start end value)))
+ 	  ((eq override t)
+ 	   ;; Override existing fontification.
+	   (font-lock-set-syntax start end value))
+ 	  ((eq override 'keep)
+ 	   ;; Keep existing fontification.
+ 	   (font-lock-fillin-text-property start end
+					   'syntax-table 'font-lock value)))))
+
+(defun font-lock-fontify-syntactic-anchored-keywords (keywords limit)
+  "Fontify according to KEYWORDS until LIMIT.
+ KEYWORDS should be of the form MATCH-ANCHORED, see `font-lock-keywords',
+ LIMIT can be modified by the value of its PRE-MATCH-FORM."
+  (let ((matcher (nth 0 keywords)) (lowdarks (nthcdr 3 keywords)) highlights
+ 	;; Evaluate PRE-MATCH-FORM.
+ 	(pre-match-value (eval (nth 1 keywords))))
+    ;; Set LIMIT to value of PRE-MATCH-FORM or the end of line.
+    (if (and (numberp pre-match-value) (> pre-match-value (point)))
+ 	(setq limit pre-match-value)
+      (save-excursion (end-of-line) (setq limit (point))))
+    (save-match-data
+      ;; Find an occurrence of `matcher' before `limit'.
+      (while (if (stringp matcher)
+ 		 (re-search-forward matcher limit t)
+ 	       (funcall matcher limit))
+ 	;; Apply each highlight to this instance of `matcher'.
+ 	(setq highlights lowdarks)
+ 	(while highlights
+ 	  (font-lock-apply-syntactic-highlight (car highlights))
+ 	  (setq highlights (cdr highlights)))))
+    ;; Evaluate POST-MATCH-FORM.
+    (eval (nth 2 keywords))))
+
+(defun font-lock-fontify-syntactic-keywords-region (start end)
+  "Fontify according to `font-lock-syntactic-keywords' between START and END.
+START should be at the beginning of a line."
+;;  ;; If `font-lock-syntactic-keywords' is a symbol, get the real keywords.
+  (when (symbolp font-lock-syntactic-keywords)
+    (setq font-lock-syntactic-keywords (font-lock-eval-keywords
+					font-lock-syntactic-keywords)))
+  ;; 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)))
+  ;; Get down to business.
+  (let ((case-fold-search font-lock-keywords-case-fold-search)
+	(keywords (cdr font-lock-syntactic-keywords))
+	keyword matcher highlights)
+    (while keywords
+      ;; Find an occurrence of `matcher' from `start' to `end'.
+      (setq keyword (car keywords) matcher (car keyword))
+      (goto-char start)
+      (while (if (stringp matcher)
+		 (re-search-forward matcher end t)
+	       (funcall matcher end))
+	;; Apply each highlight to this instance of `matcher', which may be
+	;; specific highlights or more keywords anchored to `matcher'.
+	(setq highlights (cdr keyword))
+	(while highlights
+	  (if (numberp (car (car highlights)))
+	      (font-lock-apply-syntactic-highlight (car highlights))
+	    (font-lock-fontify-syntactic-anchored-keywords (car highlights)
+							   end))
+	  (setq highlights (cdr highlights)))
+	)
+      (setq keywords (cdr keywords)))))
+
 ;;; Regexp fontification functions.
 
 (defsubst font-lock-apply-highlight (highlight)
@@ -1636,6 +1698,14 @@
 	(t				; Hopefully (MATCHER HIGHLIGHT ...)
 	 keyword)))
 
+(defun font-lock-eval-keywords (keywords)
+  ;; Evalulate KEYWORDS if a function (funcall) or variable (eval) name.
+  (if (listp keywords)
+      keywords
+    (font-lock-eval-keywords (if (fboundp keywords)
+				 (funcall keywords)
+			       (eval keywords)))))
+
 (defun font-lock-choose-keywords (keywords level)
   ;; Return LEVELth element of KEYWORDS.  A LEVEL of nil is equal to a
   ;; LEVEL of 0, a LEVEL of t is equal to (1- (length KEYWORDS)).
@@ -1713,7 +1783,7 @@
 			     (font-lock-find-font-lock-defaults major-mode)))
 	       (keywords (font-lock-choose-keywords
 			  (nth 0 defaults) font-lock-maximum-decoration)))
-	  
+
 	  ;; Keywords?
 	  (setq font-lock-keywords (if (fboundp keywords)
 				       (funcall keywords)
@@ -1779,6 +1849,7 @@
 		 (setq font-lock-beginning-of-syntax-function
 		       'beginning-of-defun)))))
 
+    (setq font-lock-cache-position (make-marker))
     (setq font-lock-defaults-computed t)))