summaryrefslogtreecommitdiff
path: root/lisp/progmodes
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2010-09-11 01:13:42 +0200
committerStefan Monnier <monnier@iro.umontreal.ca>2010-09-11 01:13:42 +0200
commitcf38dd429888fc992408716922ecab1c39242944 (patch)
tree97ee11df214e90b91f9da1620cdcec97acda8eb0 /lisp/progmodes
parent71c17aecc32c876247062fd480304b147b266130 (diff)
downloademacs-cf38dd429888fc992408716922ecab1c39242944.tar.gz
New syntax-propertize functionality.
* lisp/font-lock.el (font-lock-syntactic-keywords): Make obsolete. (font-lock-fontify-syntactic-keywords-region): Move handling of font-lock-syntactically-fontified to... (font-lock-default-fontify-region): ...here. Let syntax-propertize-function take precedence. (font-lock-fontify-syntactically-region): Cal syntax-propertize. * lisp/emacs-lisp/regexp-opt.el (regexp-opt-depth): Skip named groups. * lisp/emacs-lisp/syntax.el (syntax-propertize-function) (syntax-propertize-chunk-size, syntax-propertize--done) (syntax-propertize-extend-region-functions): New vars. (syntax-propertize-wholelines, syntax-propertize-multiline) (syntax-propertize--shift-groups, syntax-propertize-via-font-lock) (syntax-propertize): New functions. (syntax-propertize-rules): New macro. (syntax-ppss-flush-cache): Set syntax-propertize--done. (syntax-ppss): Call syntax-propertize. * lisp/progmodes/ada-mode.el (ada-set-syntax-table-properties) (ada-after-change-function, ada-initialize-syntax-table-properties) (ada-handle-syntax-table-properties): Only define when syntax-propertize is not available. (ada-mode): Use syntax-propertize-function. * lisp/progmodes/autoconf.el (autoconf-mode): Use syntax-propertize-function. (autoconf-font-lock-syntactic-keywords): Remove. * lisp/progmodes/cfengine.el (cfengine-mode): Use syntax-propertize-function. (cfengine-font-lock-syntactic-keywords): Remove. * lisp/progmodes/cperl-mode.el (cperl-mode): Use syntax-propertize-function. * lisp/progmodes/fortran.el (fortran-mode): Use syntax-propertize-function. (fortran--font-lock-syntactic-keywords): New var. (fortran-line-length): Update syntax-propertize-function and fortran--font-lock-syntactic-keywords. * lisp/progmodes/gud.el (gdb-script-syntax-propertize-function): New var; replaces gdb-script-font-lock-syntactic-keywords. (gdb-script-mode): Use it. * lisp/progmodes/js.el (js--regexp-literal): Define while compiling. (js-syntax-propertize-function): New var; replaces js-font-lock-syntactic-keywords. (js-mode): Use it. * lisp/progmodes/make-mode.el (makefile-syntax-propertize-function): New var; replaces makefile-font-lock-syntactic-keywords. (makefile-mode): Use it. (makefile-imake-mode): Adjust. * lisp/progmodes/mixal-mode.el (mixal-syntax-propertize-function): New var; replaces mixal-font-lock-syntactic-keywords. (mixal-mode): Use it. * lisp/progmodes/octave-mod.el (octave-syntax-propertize-sqs): New function to replace octave-font-lock-close-quotes. (octave-syntax-propertize-function): New function to replace octave-font-lock-syntactic-keywords. (octave-mode): Use it. * lisp/progmodes/perl-mode.el (perl-syntax-propertize-function): New fun to replace perl-font-lock-syntactic-keywords. (perl-syntax-propertize-special-constructs): New fun to replace perl-font-lock-special-syntactic-constructs. (perl-font-lock-syntactic-face-function): New fun. (perl-mode): Use it. * lisp/progmodes/python.el (python-syntax-propertize-function): New var to replace python-font-lock-syntactic-keywords. (python-mode): Use it. (python-quote-syntax): Simplify and adjust to new use. * lisp/progmodes/ruby-mode.el (ruby-here-doc-beg-re): Define while compiling. (ruby-here-doc-end-re, ruby-here-doc-beg-match) (ruby-font-lock-syntactic-keywords, ruby-comment-beg-syntax) (syntax-ppss, ruby-in-ppss-context-p, ruby-in-here-doc-p) (ruby-here-doc-find-end, ruby-here-doc-beg-syntax) (ruby-here-doc-end-syntax): Only define when syntax-propertize is not available. (ruby-syntax-propertize-function, ruby-syntax-propertize-heredoc): New functions. (ruby-in-ppss-context-p): Update to new syntax of heredocs. (electric-indent-chars): Silence bytecompiler. (ruby-mode): Use prog-mode, syntax-propertize-function, and electric-indent-chars. * lisp/progmodes/sh-script.el (sh-st-symbol): Remove. (sh-font-lock-close-heredoc, sh-font-lock-open-heredoc): Add eol arg. (sh-font-lock-flush-syntax-ppss-cache, sh-font-lock-here-doc): Remove. (sh-font-lock-quoted-subshell): Assume we've already matched $(. (sh-font-lock-paren): Set syntax-multiline. (sh-font-lock-syntactic-keywords): Remove. (sh-syntax-propertize-function): New function to replace it. (sh-mode): Use it. * lisp/progmodes/simula.el (simula-syntax-propertize-function): New var to replace simula-font-lock-syntactic-keywords. (simula-mode): Use it. * lisp/progmodes/tcl.el (tcl-syntax-propertize-function): New var to replace tcl-font-lock-syntactic-keywords. (tcl-mode): Use it. * lisp/progmodes/vhdl-mode.el (vhdl-mode): Use syntax-propertize-function if available. (vhdl-fontify-buffer): Adjust. * lisp/textmodes/bibtex.el (bibtex-mode): Use syntax-propertize-function. * lisp/textmodes/reftex.el (font-lock-syntactic-keywords): Don't declare since we don't use it. * lisp/textmodes/sgml-mode.el (sgml-syntax-propertize-function): New var to replace sgml-font-lock-syntactic-keywords. (sgml-mode): Use it. * lisp/textmodes/tex-mode.el (tex-common-initialization, doctex-mode): Use syntax-propertize-function. * lisp/textmodes/texinfo.el (texinfo-syntax-propertize-function): New fun to replace texinfo-font-lock-syntactic-keywords. (texinfo-mode): Use it. * test/indent/octave.m: Remove some `fixindent' not needed any more.
Diffstat (limited to 'lisp/progmodes')
-rw-r--r--lisp/progmodes/ada-mode.el25
-rw-r--r--lisp/progmodes/autoconf.el7
-rw-r--r--lisp/progmodes/cfengine.el20
-rw-r--r--lisp/progmodes/cperl-mode.el8
-rw-r--r--lisp/progmodes/fortran.el18
-rw-r--r--lisp/progmodes/gud.el24
-rw-r--r--lisp/progmodes/js.el24
-rw-r--r--lisp/progmodes/make-mode.el37
-rw-r--r--lisp/progmodes/mixal-mode.el23
-rw-r--r--lisp/progmodes/octave-mod.el47
-rw-r--r--lisp/progmodes/perl-mode.el334
-rw-r--r--lisp/progmodes/python.el96
-rw-r--r--lisp/progmodes/ruby-mode.el390
-rw-r--r--lisp/progmodes/sh-script.el104
-rw-r--r--lisp/progmodes/simula.el28
-rw-r--r--lisp/progmodes/tcl.el13
-rw-r--r--lisp/progmodes/vhdl-mode.el18
17 files changed, 665 insertions, 551 deletions
diff --git a/lisp/progmodes/ada-mode.el b/lisp/progmodes/ada-mode.el
index d402dd7b84a..4bbe1e43f85 100644
--- a/lisp/progmodes/ada-mode.el
+++ b/lisp/progmodes/ada-mode.el
@@ -834,10 +834,7 @@ the 4 file locations can be clicked on and jumped to."
;;
;; On Emacs, this is done through the `syntax-table' text property. The
;; corresponding action is applied automatically each time the buffer
-;; changes. If `font-lock-mode' is enabled (the default) the action is
-;; set up by `font-lock-syntactic-keywords'. Otherwise, we do it
-;; manually in `ada-after-change-function'. The proper method is
-;; installed by `ada-handle-syntax-table-properties'.
+;; changes via syntax-propertize-function.
;;
;; on XEmacs, the `syntax-table' property does not exist and we have to use a
;; slow advice to `parse-partial-sexp' to do the same thing.
@@ -937,6 +934,12 @@ declares it as a word constituent."
(insert (caddar change))
(setq change (cdr change)))))))
+(unless (eval-when-compile (fboundp 'syntax-propertize-via-font-lock))
+ ;; Before `syntax-propertize', we had to use font-lock to apply syntax-table
+ ;; properties, and in some cases we even had to do it manually (in
+ ;; `ada-after-change-function'). `ada-handle-syntax-table-properties'
+ ;; decides which method to use.
+
(defun ada-set-syntax-table-properties ()
"Assign `syntax-table' properties in accessible part of buffer.
In particular, character constants are said to be strings, #...#
@@ -991,6 +994,8 @@ OLD-LEN indicates what the length of the replaced text was."
;; Take care of `syntax-table' properties manually.
(ada-initialize-syntax-table-properties)))
+) ;;(not (fboundp 'syntax-propertize))
+
;;------------------------------------------------------------------
;; Testing the grammatical context
;;------------------------------------------------------------------
@@ -1187,8 +1192,13 @@ the file name."
'(ada-font-lock-keywords
nil t
((?\_ . "w") (?# . "."))
- beginning-of-line
- (font-lock-syntactic-keywords . ada-font-lock-syntactic-keywords)))
+ beginning-of-line))
+
+ (if (eval-when-compile (fboundp 'syntax-propertize-via-font-lock))
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-via-font-lock ada-font-lock-syntactic-keywords))
+ (set (make-local-variable 'font-lock-syntactic-keywords)
+ ada-font-lock-syntactic-keywords))
;; Set up support for find-file.el.
(set (make-local-variable 'ff-other-file-alist)
@@ -1331,7 +1341,8 @@ the file name."
;; Run this after the hook to give the users a chance to activate
;; font-lock-mode
- (unless (featurep 'xemacs)
+ (unless (or (eval-when-compile (fboundp 'syntax-propertize-via-font-lock))
+ (featurep 'xemacs))
(ada-initialize-syntax-table-properties)
(add-hook 'font-lock-mode-hook 'ada-handle-syntax-table-properties nil t))
diff --git a/lisp/progmodes/autoconf.el b/lisp/progmodes/autoconf.el
index a56623f22da..004bb3de78d 100644
--- a/lisp/progmodes/autoconf.el
+++ b/lisp/progmodes/autoconf.el
@@ -43,9 +43,6 @@
(defvar autoconf-mode-hook nil
"Hook run by `autoconf-mode'.")
-(defconst autoconf-font-lock-syntactic-keywords
- '(("\\<dnl\\>" 0 '(11))))
-
(defconst autoconf-definition-regexp
"AC_\\(SUBST\\|DEFINE\\(_UNQUOTED\\)?\\)(\\[*\\(\\sw+\\)\\]*")
@@ -94,8 +91,8 @@ searching backwards at another AC_... command."
"^[ \t]*A[CM]_\\(\\sw\\|\\s_\\)+")
(set (make-local-variable 'comment-start) "dnl ")
(set (make-local-variable 'comment-start-skip) "\\(?:\\<dnl\\|#\\) +")
- (set (make-local-variable 'font-lock-syntactic-keywords)
- autoconf-font-lock-syntactic-keywords)
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-rules ("\\<dnl\\>" (0 "<"))))
(set (make-local-variable 'font-lock-defaults)
`(autoconf-font-lock-keywords nil nil (("_" . "w"))))
(set (make-local-variable 'imenu-generic-expression)
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index 86a6be40cc5..e074e92fbe5 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -83,12 +83,6 @@ This includes those for cfservd as well as cfagent."))
;; File, acl &c in group: { token ... }
("{[ \t]*\\([^ \t\n]+\\)" 1 font-lock-constant-face)))
-(defconst cfengine-font-lock-syntactic-keywords
- ;; In the main syntax-table, backslash is marked as a punctuation, because
- ;; of its use in DOS-style directory separators. Here we try to recognize
- ;; the cases where backslash is used as an escape inside strings.
- '(("\\(\\(?:\\\\\\)+\\)\"" 1 "\\")))
-
(defvar cfengine-imenu-expression
`((nil ,(concat "^[ \t]*" (eval-when-compile
(regexp-opt cfengine-actions t))
@@ -237,13 +231,15 @@ to the action header."
(set (make-local-variable 'fill-paragraph-function)
#'cfengine-fill-paragraph)
(define-abbrev-table 'cfengine-mode-abbrev-table cfengine-mode-abbrevs)
- ;; Fixme: Use `font-lock-syntactic-keywords' to set the args of
- ;; functions in evaluated classes to string syntax, and then obey
- ;; syntax properties.
(setq font-lock-defaults
- '(cfengine-font-lock-keywords nil nil nil beginning-of-line
- (font-lock-syntactic-keywords
- . cfengine-font-lock-syntactic-keywords)))
+ '(cfengine-font-lock-keywords nil nil nil beginning-of-line))
+ ;; Fixme: set the args of functions in evaluated classes to string
+ ;; syntax, and then obey syntax properties.
+ (set (make-local-variable 'syntax-propertize-function)
+ ;; In the main syntax-table, \ is marked as a punctuation, because
+ ;; of its use in DOS-style directory separators. Here we try to
+ ;; recognize the cases where \ is used as an escape inside strings.
+ (syntax-propertize-rules ("\\(\\(?:\\\\\\)+\\)\"" (1 "\\"))))
(setq imenu-generic-expression cfengine-imenu-expression)
(set (make-local-variable 'beginning-of-defun-function)
#'cfengine-beginning-of-defun)
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index d69cce76faa..d89e41b38fb 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -1840,7 +1840,13 @@ or as help on variables `cperl-tips', `cperl-problems',
(make-local-variable 'cperl-syntax-state)
(setq cperl-syntax-state nil) ; reset syntaxification cache
(if cperl-use-syntax-table-text-property
- (progn
+ (if (boundp 'syntax-propertize-function)
+ (progn
+ ;; Reset syntaxification cache.
+ (set (make-local-variable 'cperl-syntax-done-to) nil)
+ (set (make-local-variable 'syntax-propertize-function)
+ (lambda (start end)
+ (goto-char start) (cperl-fontify-syntaxically end))))
(make-local-variable 'parse-sexp-lookup-properties)
;; Do not introduce variable if not needed, we check it!
(set 'parse-sexp-lookup-properties t)
diff --git a/lisp/progmodes/fortran.el b/lisp/progmodes/fortran.el
index 2002f05003d..daa0fd07364 100644
--- a/lisp/progmodes/fortran.el
+++ b/lisp/progmodes/fortran.el
@@ -483,6 +483,7 @@ The only difference is, it returns t in a case when the default returns nil."
"Maximum highlighting for Fortran mode.
Consists of level 3 plus all other intrinsics not already highlighted.")
+(defvar fortran--font-lock-syntactic-keywords)
;; Comments are real pain in Fortran because there is no way to
;; represent the standard comment syntax in an Emacs syntax table.
;; (We can do so for F90-style). Therefore an unmatched quote in a
@@ -887,9 +888,11 @@ with no args, if that value is non-nil."
fortran-font-lock-keywords-3
fortran-font-lock-keywords-4)
nil t ((?/ . "$/") ("_$" . "w"))
- fortran-beginning-of-subprogram
- (font-lock-syntactic-keywords
- . fortran-font-lock-syntactic-keywords)))
+ fortran-beginning-of-subprogram))
+ (set (make-local-variable 'fortran--font-lock-syntactic-keywords)
+ (fortran-make-syntax-propertize-function))
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-via-font-lock fortran--font-lock-syntactic-keywords))
(set (make-local-variable 'imenu-case-fold-search) t)
(set (make-local-variable 'imenu-generic-expression)
fortran-imenu-generic-expression)
@@ -917,10 +920,13 @@ affects all Fortran buffers, and also the default."
(when (eq major-mode 'fortran-mode)
(setq fortran-line-length nchars
fill-column fortran-line-length
- new (fortran-font-lock-syntactic-keywords))
+ new (fortran-make-syntax-propertize-function))
;; Refontify only if necessary.
- (unless (equal new font-lock-syntactic-keywords)
- (setq font-lock-syntactic-keywords new)
+ (unless (equal new fortran--font-lock-syntactic-keywords)
+ (setq fortran--font-lock-syntactic-keywords new)
+ (setq syntax-propertize-function
+ (syntax-propertize-via-font-lock new))
+ (syntax-ppss-flush-cache (point-min))
(if font-lock-mode (font-lock-mode 1))))))
(if global
(buffer-list)
diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el
index d20a14682c7..4c1471e39ec 100644
--- a/lisp/progmodes/gud.el
+++ b/lisp/progmodes/gud.el
@@ -3123,10 +3123,12 @@ class of the file (using s to separate nested class ids)."
("\\$\\(\\w+\\)" (1 font-lock-variable-name-face))
("^\\s-*\\(\\w\\(\\w\\|\\s_\\)*\\)" (1 font-lock-keyword-face))))
-(defvar gdb-script-font-lock-syntactic-keywords
- '(("^document\\s-.*\\(\n\\)" (1 "< b"))
- ("^end\\>"
- (0 (unless (eq (match-beginning 0) (point-min))
+(defconst gdb-script-syntax-propertize-function
+ (syntax-propertize-rules
+ ("^document\\s-.*\\(\n\\)" (1 "< b"))
+ ("^end\\(\\>\\)"
+ (1 (ignore
+ (unless (eq (match-beginning 0) (point-min))
;; We change the \n in front, which is more difficult, but results
;; in better highlighting. If the doc is empty, the single \n is
;; both the beginning and the end of the docstring, which can't be
@@ -3138,10 +3140,9 @@ class of the file (using s to separate nested class ids)."
'syntax-table (eval-when-compile
(string-to-syntax "> b")))
;; Make sure that rehighlighting the previous line won't erase our
- ;; syntax-table property.
+ ;; syntax-table property and that modifying `end' will.
(put-text-property (1- (match-beginning 0)) (match-end 0)
- 'font-lock-multiline t)
- nil)))))
+ 'syntax-multiline t)))))))
(defun gdb-script-font-lock-syntactic-face (state)
(cond
@@ -3239,10 +3240,13 @@ Treats actions as defuns."
#'gdb-script-end-of-defun)
(set (make-local-variable 'font-lock-defaults)
'(gdb-script-font-lock-keywords nil nil ((?_ . "w")) nil
- (font-lock-syntactic-keywords
- . gdb-script-font-lock-syntactic-keywords)
(font-lock-syntactic-face-function
- . gdb-script-font-lock-syntactic-face))))
+ . gdb-script-font-lock-syntactic-face)))
+ ;; Recognize docstrings.
+ (set (make-local-variable 'syntax-propertize-function)
+ gdb-script-syntax-propertize-function)
+ (add-hook 'syntax-propertize-extend-region-functions
+ #'syntax-propertize-multiline 'append 'local))
;;; tooltips for GUD
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index c49f86e2d0b..ba70bb8ecce 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -1660,18 +1660,19 @@ This performs fontification according to `js--class-styles'."
;; XXX: Javascript can continue a regexp literal across lines so long
;; as the newline is escaped with \. Account for that in the regexp
;; below.
-(defconst js--regexp-literal
+(eval-and-compile
+ (defconst js--regexp-literal
"[=(,:]\\(?:\\s-\\|\n\\)*\\(/\\)\\(?:\\\\/\\|[^/*]\\)\\(?:\\\\/\\|[^/]\\)*\\(/\\)"
"Regexp matching a JavaScript regular expression literal.
Match groups 1 and 2 are the characters forming the beginning and
-end of the literal.")
+end of the literal."))
+
-;; we want to match regular expressions only at the beginning of
-;; expressions
-(defconst js-font-lock-syntactic-keywords
- `((,js--regexp-literal (1 "|") (2 "|")))
- "Syntactic font lock keywords matching regexps in JavaScript.
-See `font-lock-keywords'.")
+(defconst js-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; We want to match regular expressions only at the beginning of
+ ;; expressions.
+ (js--regexp-literal (1 "\"") (2 "\""))))
;;; Indentation
@@ -3303,10 +3304,9 @@ Key bindings:
(set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
(set (make-local-variable 'font-lock-defaults)
- (list js--font-lock-keywords
- nil nil nil nil
- '(font-lock-syntactic-keywords
- . js-font-lock-syntactic-keywords)))
+ '(js--font-lock-keywords))
+ (set (make-local-variable 'syntax-propertize-function)
+ js-syntax-propertize-function)
(set (make-local-variable 'parse-sexp-ignore-comments) t)
(set (make-local-variable 'parse-sexp-lookup-properties) t)
diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el
index 362a1db6c10..187c838382b 100644
--- a/lisp/progmodes/make-mode.el
+++ b/lisp/progmodes/make-mode.el
@@ -505,15 +505,16 @@ not be enclosed in { } or ( )."
cpp-font-lock-keywords))
-(defconst makefile-font-lock-syntactic-keywords
- ;; From sh-script.el.
- ;; A `#' begins a comment in sh when it is unquoted and at the beginning
- ;; of a word. In the shell, words are separated by metacharacters.
- ;; The list of special chars is taken from the single-unix spec of the
- ;; shell command language (under `quoting') but with `$' removed.
- '(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 "_")
- ;; Change the syntax of a quoted newline so that it does not end a comment.
- ("\\\\\n" 0 ".")))
+(defconst makefile-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; From sh-script.el.
+ ;; A `#' begins a comment in sh when it is unquoted and at the beginning
+ ;; of a word. In the shell, words are separated by metacharacters.
+ ;; The list of special chars is taken from the single-unix spec of the
+ ;; shell command language (under `quoting') but with `$' removed.
+ ("[^|&;<>()`\\\"' \t\n]\\(#+\\)" (1 "_"))
+ ;; Change the syntax of a quoted newline so that it does not end a comment.
+ ("\\\\\n" (0 "."))))
(defvar makefile-imenu-generic-expression
`(("Dependencies" makefile-previous-dependency 1)
@@ -872,9 +873,9 @@ Makefile mode can be configured by modifying the following variables:
'(makefile-font-lock-keywords
nil nil
((?$ . "."))
- backward-paragraph
- (font-lock-syntactic-keywords
- . makefile-font-lock-syntactic-keywords)))
+ backward-paragraph))
+ (set (make-local-variable 'syntax-propertize-function)
+ makefile-syntax-propertize-function)
;; Add-log.
(set (make-local-variable 'add-log-current-defun-function)
@@ -943,15 +944,9 @@ Makefile mode can be configured by modifying the following variables:
(define-derived-mode makefile-imake-mode makefile-mode "Imakefile"
"An adapted `makefile-mode' that knows about imake."
:syntax-table makefile-imake-mode-syntax-table
- (let ((base `(makefile-imake-font-lock-keywords ,@(cdr font-lock-defaults)))
- new)
- ;; Remove `font-lock-syntactic-keywords' entry from font-lock-defaults.
- (mapc (lambda (elt)
- (unless (and (consp elt)
- (eq (car elt) 'font-lock-syntactic-keywords))
- (setq new (cons elt new))))
- base)
- (setq font-lock-defaults (nreverse new))))
+ (set (make-local-variable 'syntax-propertize-function) nil)
+ (setq font-lock-defaults
+ `(makefile-imake-font-lock-keywords ,@(cdr font-lock-defaults))))
diff --git a/lisp/progmodes/mixal-mode.el b/lisp/progmodes/mixal-mode.el
index ecb8461a9f2..94af563d88f 100644
--- a/lisp/progmodes/mixal-mode.el
+++ b/lisp/progmodes/mixal-mode.el
@@ -89,7 +89,7 @@
(defvar mixal-mode-syntax-table
(let ((st (make-syntax-table)))
;; We need to do a bit more to make fontlocking for comments work.
- ;; See mixal-font-lock-syntactic-keywords.
+ ;; See use of syntax-propertize-function.
;; (modify-syntax-entry ?* "<" st)
(modify-syntax-entry ?\n ">" st)
st)
@@ -1028,13 +1028,14 @@ EXECUTION-TIME holds info about the time it takes, number or string.")
;;; Font-locking:
-(defvar mixal-font-lock-syntactic-keywords
- ;; Normal comments start with a * in column 0 and end at end of line.
- '(("^\\*" (0 '(11))) ;(string-to-syntax "<") == '(11)
- ;; Every line can end with a comment which is placed after the operand.
- ;; I assume here that mnemonics without operands can not have a comment.
- ("^[[:alnum:]]*[ \t]+[[:alnum:]]+[ \t]+[^ \n\t]+[ \t]*\\([ \t]\\)[^\n \t]"
- (1 '(11)))))
+(defconst mixal-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; Normal comments start with a * in column 0 and end at end of line.
+ ("^\\*" (0 "<"))
+ ;; Every line can end with a comment which is placed after the operand.
+ ;; I assume here that mnemonics without operands can not have a comment.
+ ("^[[:alnum:]]*[ \t]+[[:alnum:]]+[ \t]+[^ \n\t]+[ \t]*\\([ \t]\\)[^\n \t]"
+ (1 "<"))))
(defvar mixal-font-lock-keywords
`(("^\\([A-Z0-9a-z]+\\)"
@@ -1110,9 +1111,9 @@ Assumes that file has been compiled with debugging support."
(set (make-local-variable 'comment-start) "*")
(set (make-local-variable 'comment-start-skip) "^\\*[ \t]*")
(set (make-local-variable 'font-lock-defaults)
- `(mixal-font-lock-keywords nil nil nil nil
- (font-lock-syntactic-keywords . ,mixal-font-lock-syntactic-keywords)
- (parse-sexp-lookup-properties . t)))
+ `(mixal-font-lock-keywords))
+ (set (make-local-variable 'syntax-propertize-function)
+ mixal-syntax-propertize-function)
;; might add an indent function in the future
;; (set (make-local-variable 'indent-line-function) 'mixal-indent-line)
(set (make-local-variable 'compile-command) (concat "mixasm "
diff --git a/lisp/progmodes/octave-mod.el b/lisp/progmodes/octave-mod.el
index 5d17e48ada7..bbefdaa2ccf 100644
--- a/lisp/progmodes/octave-mod.el
+++ b/lisp/progmodes/octave-mod.el
@@ -179,38 +179,28 @@ parenthetical grouping.")
'(3 font-lock-function-name-face nil t)))
"Additional Octave expressions to highlight.")
-(defvar octave-font-lock-syntactic-keywords
+(defun octave-syntax-propertize-function (start end)
+ (goto-char start)
+ (octave-syntax-propertize-sqs end)
+ (funcall (syntax-propertize-rules
;; Try to distinguish the string-quotes from the transpose-quotes.
- '(("[[({,; ]\\('\\)" (1 "\"'"))
- (octave-font-lock-close-quotes)))
-
-(defun octave-font-lock-close-quotes (limit)
- "Fix the syntax-table of the closing quotes of single-quote strings."
- ;; Freely inspired from perl-font-lock-special-syntactic-constructs.
- (let ((state (syntax-ppss)))
- (while (< (point) limit)
- (cond
- ((eq (nth 3 state) ?\')
+ ("[[({,; ]\\('\\)"
+ (1 (prog1 "\"'" (octave-syntax-propertize-sqs end)))))
+ (point) end))
+
+(defun octave-syntax-propertize-sqs (end)
+ "Propertize the content/end of single-quote strings."
+ (when (eq (nth 3 (syntax-ppss)) ?\')
;; A '..' string.
- (save-excursion
- (when (re-search-forward "\\(?:\\=\\|[^']\\)\\(?:''\\)*\\('\\)[^']"
- nil t)
- (goto-char (1- (point)))
- ;; Remove any syntax-table property we may have applied to
- ;; some of the (doubled) single quotes within the string.
- ;; Since these are the only chars on which we place properties,
- ;; we take a shortcut and just remove all properties.
- (remove-text-properties (1+ (nth 8 state)) (match-beginning 1)
- '(syntax-table nil))
+ (when (re-search-forward
+ "\\(?:\\=\\|[^']\\)\\(?:''\\)*\\('\\)\\($\\|[^']\\)" end 'move)
+ (goto-char (match-beginning 2))
(when (eq (char-before (match-beginning 1)) ?\\)
;; Backslash cannot escape a single quote.
(put-text-property (1- (match-beginning 1)) (match-beginning 1)
'syntax-table (string-to-syntax ".")))
(put-text-property (match-beginning 1) (match-end 1)
- 'syntax-table (string-to-syntax "\"'"))))))
-
- (setq state (parse-partial-sexp (point) limit nil nil state
- 'syntax-table)))))
+ 'syntax-table (string-to-syntax "\"'")))))
(defcustom inferior-octave-buffer "*Inferior Octave*"
"Name of buffer for running an inferior Octave process."
@@ -684,9 +674,10 @@ including a reproducible test case and send the message."
(set (make-local-variable 'normal-auto-fill-function) 'octave-auto-fill)
(set (make-local-variable 'font-lock-defaults)
- '(octave-font-lock-keywords nil nil nil nil
- (font-lock-syntactic-keywords . octave-font-lock-syntactic-keywords)
- (parse-sexp-lookup-properties . t)))
+ '(octave-font-lock-keywords))
+
+ (set (make-local-variable 'syntax-propertize-function)
+ #'octave-syntax-propertize-function)
(set (make-local-variable 'imenu-generic-expression)
octave-mode-imenu-generic-expression)
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index f8eba5accdb..ae3acc3cda3 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -250,59 +250,76 @@ The expansion is entirely correct because it uses the C preprocessor."
;; y /.../.../
;;
;; <file*glob>
-(defvar perl-font-lock-syntactic-keywords
- ;; TODO: here-documents ("<<\\(\\sw\\|['\"]\\)")
- `(;; Turn POD into b-style comments
- ("^\\(=\\)\\sw" (1 "< b"))
- ("^=cut[ \t]*\\(\n\\)" (1 "> b"))
- ;; Catch ${ so that ${var} doesn't screw up indentation.
- ;; This also catches $' to handle 'foo$', although it should really
- ;; check that it occurs inside a '..' string.
- ("\\(\\$\\)[{']" (1 ". p"))
- ;; Handle funny names like $DB'stop.
- ("\\$ ?{?^?[_a-zA-Z][_a-zA-Z0-9]*\\('\\)[_a-zA-Z]" (1 "_"))
- ;; format statements
- ("^[ \t]*format.*=[ \t]*\\(\n\\)" (1 '(7)))
- ;; Funny things in `sub' arg-specs like `sub myfun ($)' or `sub ($)'.
- ;; Be careful not to match "sub { (...) ... }".
- ("\\<sub\\(?:[[:space:]]+[^{}[:punct:][:space:]]+\\)?[[:space:]]*(\\([^)]+\\))"
- 1 '(1))
- ;; Regexp and funny quotes. Distinguishing a / that starts a regexp
- ;; match from the division operator is ...interesting.
- ;; Basically, / is a regexp match if it's preceded by an infix operator
- ;; (or some similar separator), or by one of the special keywords
- ;; corresponding to builtin functions that can take their first arg
- ;; without parentheses. Of course, that presume we're looking at the
- ;; *opening* slash. We can afford to mis-match the closing ones
- ;; here, because they will be re-treated separately later in
- ;; perl-font-lock-special-syntactic-constructs.
- (,(concat "\\(?:\\(?:\\(?:^\\|[^$@&%[:word:]]\\)"
- (regexp-opt '("split" "if" "unless" "until" "while" "split"
- "grep" "map" "not" "or" "and"))
- "\\)\\|[?:.,;=!~({[]\\|\\(^\\)\\)[ \t\n]*\\(/\\)")
- (2 (if (and (match-end 1)
- (save-excursion
- (goto-char (match-end 1))
- ;; Not 100% correct since we haven't finished setting up
- ;; the syntax-table before point, but better than nothing.
- (forward-comment (- (point-max)))
- (put-text-property (point) (match-end 2)
- 'jit-lock-defer-multiline t)
- (not (memq (char-before)
- '(?? ?: ?. ?, ?\; ?= ?! ?~ ?\( ?\[)))))
- nil ;; A division sign instead of a regexp-match.
- '(7))))
- ("\\(^\\|[?:.,;=!~({[ \t]\\)\\([msy]\\|q[qxrw]?\\|tr\\)\\>\\s-*\\([^])}> \n\t]\\)"
- ;; Nasty cases:
- ;; /foo/m $a->m $#m $m @m %m
- ;; \s (appears often in regexps).
- ;; -s file
- (3 (if (assoc (char-after (match-beginning 3))
- perl-quote-like-pairs)
- '(15) '(7))))
- ;; Find and mark the end of funny quotes and format statements.
- (perl-font-lock-special-syntactic-constructs)
- ))
+(defun perl-syntax-propertize-function (start end)
+ (let ((case-fold-search nil))
+ (goto-char start)
+ (perl-syntax-propertize-special-constructs end)
+ ;; TODO: here-documents ("<<\\(\\sw\\|['\"]\\)")
+ (funcall
+ (syntax-propertize-rules
+ ;; Turn POD into b-style comments. Place the cut rule first since it's
+ ;; more specific.
+ ("^=cut\\>.*\\(\n\\)" (1 "> b"))
+ ("^\\(=\\)\\sw" (1 "< b"))
+ ;; Catch ${ so that ${var} doesn't screw up indentation.
+ ;; This also catches $' to handle 'foo$', although it should really
+ ;; check that it occurs inside a '..' string.
+ ("\\(\\$\\)[{']" (1 ". p"))
+ ;; Handle funny names like $DB'stop.
+ ("\\$ ?{?^?[_a-zA-Z][_a-zA-Z0-9]*\\('\\)[_a-zA-Z]" (1 "_"))
+ ;; format statements
+ ("^[ \t]*format.*=[ \t]*\\(\n\\)"
+ (1 (prog1 "\"" (perl-syntax-propertize-special-constructs end))))
+ ;; Funny things in `sub' arg-specs like `sub myfun ($)' or `sub ($)'.
+ ;; Be careful not to match "sub { (...) ... }".
+ ("\\<sub\\(?:[[:space:]]+[^{}[:punct:][:space:]]+\\)?[[:space:]]*(\\([^)]+\\))"
+ (1 "."))
+ ;; Regexp and funny quotes. Distinguishing a / that starts a regexp
+ ;; match from the division operator is ...interesting.
+ ;; Basically, / is a regexp match if it's preceded by an infix operator
+ ;; (or some similar separator), or by one of the special keywords
+ ;; corresponding to builtin functions that can take their first arg
+ ;; without parentheses. Of course, that presume we're looking at the
+ ;; *opening* slash. We can afford to mis-match the closing ones
+ ;; here, because they will be re-treated separately later in
+ ;; perl-font-lock-special-syntactic-constructs.
+ ((concat "\\(?:\\(?:^\\|[^$@&%[:word:]]\\)"
+ (regexp-opt '("split" "if" "unless" "until" "while" "split"
+ "grep" "map" "not" "or" "and"))
+ "\\|[?:.,;=!~({[]\\|\\(^\\)\\)[ \t\n]*\\(/\\)")
+ (2 (ignore
+ (if (and (match-end 1) ; / at BOL.
+ (save-excursion
+ (goto-char (match-end 1))
+ (forward-comment (- (point-max)))
+ (put-text-property (point) (match-end 2)
+ 'syntax-multiline t)
+ (not (memq (char-before)
+ '(?? ?: ?. ?, ?\; ?= ?! ?~ ?\( ?\[)))))
+ nil ;; A division sign instead of a regexp-match.
+ (put-text-property (match-beginning 2) (match-end 2)
+ 'syntax-table (string-to-syntax "\""))
+ (perl-syntax-propertize-special-constructs end)))))
+ ("\\(^\\|[?:.,;=!~({[ \t]\\)\\([msy]\\|q[qxrw]?\\|tr\\)\\>\\s-*\\([^])}> \n\t]\\)"
+ ;; Nasty cases:
+ ;; /foo/m $a->m $#m $m @m %m
+ ;; \s (appears often in regexps).
+ ;; -s file
+ ;; sub tr {...}
+ (3 (ignore
+ (if (save-excursion (goto-char (match-beginning 0))
+ (forward-word -1)
+ (looking-at-p "sub[ \t\n]"))
+ ;; This is defining a function.
+ nil
+ (put-text-property (match-beginning 3) (match-end 3)
+ 'syntax-table
+ (if (assoc (char-after (match-beginning 3))
+ perl-quote-like-pairs)
+ (string-to-syntax "|")
+ (string-to-syntax "\"")))
+ (perl-syntax-propertize-special-constructs end))))))
+ (point) end)))
(defvar perl-empty-syntax-table
(let ((st (copy-syntax-table)))
@@ -321,95 +338,123 @@ The expansion is entirely correct because it uses the C preprocessor."
(modify-syntax-entry close ")" st))
st))
-(defun perl-font-lock-special-syntactic-constructs (limit)
- ;; We used to do all this in a font-lock-syntactic-face-function, which
- ;; did not work correctly because sometimes some parts of the buffer are
- ;; treated with font-lock-syntactic-keywords but not with
- ;; font-lock-syntactic-face-function (mostly because of
- ;; font-lock-syntactically-fontified). That meant that some syntax-table
- ;; properties were missing. So now we do the parse-partial-sexp loop
- ;; ourselves directly from font-lock-syntactic-keywords, so we're sure
- ;; it's done when necessary.
+(defun perl-syntax-propertize-special-constructs (limit)
+ "Propertize special constructs like regexps and formats."
(let ((state (syntax-ppss))
char)
- (while (< (point) limit)
- (cond
- ((or (null (setq char (nth 3 state)))
- (and (characterp char) (eq (char-syntax (nth 3 state)) ?\")))
- ;; Normal text, or comment, or docstring, or normal string.
- nil)
- ((eq (nth 3 state) ?\n)
- ;; A `format' command.
- (save-excursion
- (when (and (re-search-forward "^\\s *\\.\\s *$" nil t)
- (not (eobp)))
- (put-text-property (point) (1+ (point)) 'syntax-table '(7)))))
- (t
- ;; This is regexp like quote thingy.
- (setq char (char-after (nth 8 state)))
- (save-excursion
- (let ((twoargs (save-excursion
- (goto-char (nth 8 state))
- (skip-syntax-backward " ")
- (skip-syntax-backward "w")
- (member (buffer-substring
- (point) (progn (forward-word 1) (point)))
- '("tr" "s" "y"))))
- (close (cdr (assq char perl-quote-like-pairs)))
- (pos (point))
- (st (perl-quote-syntax-table char)))
- (if (not close)
- ;; The closing char is the same as the opening char.
- (with-syntax-table st
- (parse-partial-sexp (point) (point-max)
- nil nil state 'syntax-table)
- (when twoargs
- (parse-partial-sexp (point) (point-max)
- nil nil state 'syntax-table)))
- ;; The open/close chars are matched like () [] {} and <>.
- (let ((parse-sexp-lookup-properties nil))
- (condition-case err
- (progn
- (with-syntax-table st
- (goto-char (nth 8 state)) (forward-sexp 1))
- (when twoargs
- (save-excursion
- ;; Skip whitespace and make sure that font-lock will
- ;; refontify the second part in the proper context.
- (put-text-property
- (point) (progn (forward-comment (point-max)) (point))
- 'font-lock-multiline t)
- ;;
- (unless
- (or (eobp)
- (save-excursion
- (with-syntax-table
- (perl-quote-syntax-table (char-after))
- (forward-sexp 1))
- (put-text-property pos (line-end-position)
- 'jit-lock-defer-multiline t)
- (looking-at "\\s-*\\sw*e")))
- (put-text-property (point) (1+ (point))
- 'syntax-table
- (if (assoc (char-after)
- perl-quote-like-pairs)
- '(15) '(7)))))))
- ;; The arg(s) is not terminated, so it extends until EOB.
- (scan-error (goto-char (point-max))))))
- ;; Point is now right after the arg(s).
- ;; Erase any syntactic marks within the quoted text.
- (put-text-property pos (1- (point)) 'syntax-table nil)
- (when (eq (char-before (1- (point))) ?$)
- (put-text-property (- (point) 2) (1- (point))
- 'syntax-table '(1)))
- (put-text-property (1- (point)) (point)
- 'syntax-table (if close '(15) '(7)))))))
-
- (setq state (parse-partial-sexp (point) limit nil nil state
- 'syntax-table))))
- ;; Tell font-lock that this needs not further processing.
- nil)
-
+ (cond
+ ((or (null (setq char (nth 3 state)))
+ (and (characterp char) (eq (char-syntax (nth 3 state)) ?\")))
+ ;; Normal text, or comment, or docstring, or normal string.
+ nil)
+ ((eq (nth 3 state) ?\n)
+ ;; A `format' command.
+ (when (re-search-forward "^\\s *\\.\\s *\n" limit 'move)
+ (put-text-property (1- (point)) (point)
+ 'syntax-table (string-to-syntax "\""))))
+ (t
+ ;; This is regexp like quote thingy.
+ (setq char (char-after (nth 8 state)))
+ (let ((twoargs (save-excursion
+ (goto-char (nth 8 state))
+ (skip-syntax-backward " ")
+ (skip-syntax-backward "w")
+ (member (buffer-substring
+ (point) (progn (forward-word 1) (point)))
+ '("tr" "s" "y"))))
+ (close (cdr (assq char perl-quote-like-pairs)))
+ (st (perl-quote-syntax-table char)))
+ (when (with-syntax-table st
+ (if close
+ ;; For paired delimiters, Perl allows nesting them, but
+ ;; since we treat them as strings, Emacs does not count
+ ;; those delimiters in `state', so we don't know how deep
+ ;; we are: we have to go back to the beginning of this
+ ;; "string" and count from there.
+ (condition-case nil
+ (progn
+ ;; Start after the first char since it doesn't have
+ ;; paren-syntax (an alternative would be to let-bind
+ ;; parse-sexp-lookup-properties).
+ (goto-char (1+ (nth 8 state)))
+ (up-list 1)
+ t)
+ (scan-error nil))
+ (not (or (nth 8 (parse-partial-sexp
+ (point) limit nil nil state 'syntax-table))
+ ;; If we have a self-paired opener and a twoargs
+ ;; command, the form is s/../../ so we have to skip
+ ;; a second time.
+ ;; In the case of s{...}{...}, we only handle the
+ ;; first part here and the next below.
+ (when (and twoargs (not close))
+ (nth 8 (parse-partial-sexp
+ (point) limit
+ nil nil state 'syntax-table)))))))
+ ;; Point is now right after the arg(s).
+ (when (eq (char-before (1- (point))) ?$)
+ (put-text-property (- (point) 2) (1- (point))
+ 'syntax-table '(1)))
+ (put-text-property (1- (point)) (point)
+ 'syntax-table
+ (if close
+ (string-to-syntax "|")
+ (string-to-syntax "\"")))
+ ;; If we have two args with a non-self-paired starter (e.g.
+ ;; s{...}{...}) we're right after the first arg, so we still have to
+ ;; handle the second part.
+ (when (and twoargs close)
+ ;; Skip whitespace and make sure that font-lock will
+ ;; refontify the second part in the proper context.
+ (put-text-property
+ (point) (progn (forward-comment (point-max)) (point))
+ 'syntax-multiline t)
+ ;;
+ (when (< (point) limit)
+ (put-text-property (point) (1+ (point))
+ 'syntax-table
+ (if (assoc (char-after)
+ perl-quote-like-pairs)
+ ;; Put an `e' in the cdr to mark this
+ ;; char as "second arg starter".
+ (string-to-syntax "|e")
+ (string-to-syntax "\"e")))
+ (forward-char 1)
+ ;; Re-use perl-syntax-propertize-special-constructs to handle the
+ ;; second part (the first delimiter of second part can't be
+ ;; preceded by "s" or "tr" or "y", so it will not be considered
+ ;; as twoarg).
+ (perl-syntax-propertize-special-constructs limit)))))))))
+
+(defun perl-font-lock-syntactic-face-function (state)
+ (cond
+ ((and (nth 3 state)
+ (eq ?e (cdr-safe (get-text-property (nth 8 state) 'syntax-table)))
+ ;; This is a second-arg of s{..}{...} form; let's check if this second
+ ;; arg is executable code rather than a string. For that, we need to
+ ;; look for an "e" after this second arg, so we have to hunt for the
+ ;; end of the arg. Depending on whether the whole arg has already
+ ;; been syntax-propertized or not, the end-char will have different
+ ;; syntaxes, so let's ignore syntax-properties temporarily so we can
+ ;; pretend it has not been syntax-propertized yet.
+ (let* ((parse-sexp-lookup-properties nil)
+ (char (char-after (nth 8 state)))
+ (paired (assq char perl-quote-like-pairs)))
+ (with-syntax-table (perl-quote-syntax-table char)
+ (save-excursion
+ (if (not paired)
+ (parse-partial-sexp (point) (point-max)
+ nil nil state 'syntax-table)
+ (condition-case nil
+ (progn
+ (goto-char (1+ (nth 8 state)))
+ (up-list 1))
+ (scan-error (goto-char (point-max)))))
+ (put-text-property (nth 8 state) (point)
+ 'jit-lock-defer-multiline t)
+ (looking-at "[ \t]*\\sw*e")))))
+ nil)
+ (t (funcall (default-value 'font-lock-syntactic-face-function) state))))
(defcustom perl-indent-level 4
"*Indentation of Perl statements with respect to containing block."
@@ -574,9 +619,12 @@ Turning on Perl mode runs the normal hook `perl-mode-hook'."
perl-font-lock-keywords-1
perl-font-lock-keywords-2)
nil nil ((?\_ . "w")) nil
- (font-lock-syntactic-keywords
- . perl-font-lock-syntactic-keywords)
- (parse-sexp-lookup-properties . t)))
+ (font-lock-syntactic-face-function
+ . perl-font-lock-syntactic-face-function)))
+ (set (make-local-variable 'syntax-propertize-function)
+ #'perl-syntax-propertize-function)
+ (add-hook 'syntax-propertize-extend-region-functions
+ #'syntax-propertize-multiline 'append 'local)
;; Tell imenu how to handle Perl.
(set (make-local-variable 'imenu-generic-expression)
perl-imenu-generic-expression)
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 2f65ffa1e17..10e852223ce 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -166,29 +166,32 @@
symbol-end)
. font-lock-builtin-face)))
-(defconst python-font-lock-syntactic-keywords
+(defconst python-syntax-propertize-function
;; Make outer chars of matching triple-quote sequences into generic
;; string delimiters. Fixme: Is there a better way?
;; First avoid a sequence preceded by an odd number of backslashes.
- `((,(rx (not (any ?\\))
- ?\\ (* (and ?\\ ?\\))
- (group (syntax string-quote))
- (backref 1)
- (group (backref 1)))
- (2 ,(string-to-syntax "\""))) ; dummy
- (,(rx (group (optional (any "uUrR"))) ; prefix gets syntax property
- (optional (any "rR")) ; possible second prefix
- (group (syntax string-quote)) ; maybe gets property
- (backref 2) ; per first quote
- (group (backref 2))) ; maybe gets property
- (1 (python-quote-syntax 1))
- (2 (python-quote-syntax 2))
- (3 (python-quote-syntax 3)))
- ;; This doesn't really help.
-;;; (,(rx (and ?\\ (group ?\n))) (1 " "))
- ))
-
-(defun python-quote-syntax (n)
+ (syntax-propertize-rules
+ (;; (rx (not (any ?\\))
+ ;; ?\\ (* (and ?\\ ?\\))
+ ;; (group (syntax string-quote))
+ ;; (backref 1)
+ ;; (group (backref 1)))
+ ;; ĦBackrefs don't work in syntax-propertize-rules!
+ "[^\\]\\\\\\(\\\\\\\\\\)*\\(?:''\\('\\)\\|\"\"\\(?2:\"\\)\\)"
+ (2 "\"")) ; dummy
+ (;; (rx (optional (group (any "uUrR"))) ; prefix gets syntax property
+ ;; (optional (any "rR")) ; possible second prefix
+ ;; (group (syntax string-quote)) ; maybe gets property
+ ;; (backref 2) ; per first quote
+ ;; (group (backref 2))) ; maybe gets property
+ ;; ĦBackrefs don't work in syntax-propertize-rules!
+ "\\([RUru]\\)?[Rr]?\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)"
+ (3 (ignore (python-quote-syntax))))
+ ;; This doesn't really help.
+ ;;((rx (and ?\\ (group ?\n))) (1 " "))
+ ))
+
+(defun python-quote-syntax ()
"Put `syntax-table' property correctly on triple quote.
Used for syntactic keywords. N is the match number (1, 2 or 3)."
;; Given a triple quote, we have to check the context to know
@@ -206,28 +209,25 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)."
;; x '"""' x """ \"""" x
(save-excursion
(goto-char (match-beginning 0))
- (cond
- ;; Consider property for the last char if in a fenced string.
- ((= n 3)
- (let* ((font-lock-syntactic-keywords nil)
- (syntax (syntax-ppss)))
- (when (eq t (nth 3 syntax)) ; after unclosed fence
- (goto-char (nth 8 syntax)) ; fence position
- (skip-chars-forward "uUrR") ; skip any prefix
- ;; Is it a matching sequence?
- (if (eq (char-after) (char-after (match-beginning 2)))
- (eval-when-compile (string-to-syntax "|"))))))
- ;; Consider property for initial char, accounting for prefixes.
- ((or (and (= n 2) ; leading quote (not prefix)
- (= (match-beginning 1) (match-end 1))) ; prefix is null
- (and (= n 1) ; prefix
- (/= (match-beginning 1) (match-end 1)))) ; non-empty
- (let ((font-lock-syntactic-keywords nil))
- (unless (eq 'string (syntax-ppss-context (syntax-ppss)))
- (eval-when-compile (string-to-syntax "|")))))
- ;; Otherwise (we're in a non-matching string) the property is
- ;; nil, which is OK.
- )))
+ (let ((syntax (save-match-data (syntax-ppss))))
+ (cond
+ ((eq t (nth 3 syntax)) ; after unclosed fence
+ ;; Consider property for the last char if in a fenced string.
+ (goto-char (nth 8 syntax)) ; fence position
+ (skip-chars-forward "uUrR") ; skip any prefix
+ ;; Is it a matching sequence?
+ (if (eq (char-after) (char-after (match-beginning 2)))
+ (put-text-property (match-beginning 3) (match-end 3)
+ 'syntax-table (string-to-syntax "|"))))
+ ((match-end 1)
+ ;; Consider property for initial char, accounting for prefixes.
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'syntax-table (string-to-syntax "|")))
+ (t
+ ;; Consider property for initial char, accounting for prefixes.
+ (put-text-property (match-beginning 2) (match-end 2)
+ 'syntax-table (string-to-syntax "|"))))
+ )))
;; This isn't currently in `font-lock-defaults' as probably not worth
;; it -- we basically only mess with a few normally-symbol characters.
@@ -2495,12 +2495,12 @@ with skeleton expansions for compound statement templates.
:group 'python
(set (make-local-variable 'font-lock-defaults)
'(python-font-lock-keywords nil nil nil nil
- (font-lock-syntactic-keywords
- . python-font-lock-syntactic-keywords)
- ;; This probably isn't worth it.
- ;; (font-lock-syntactic-face-function
- ;; . python-font-lock-syntactic-face-function)
- ))
+ ;; This probably isn't worth it.
+ ;; (font-lock-syntactic-face-function
+ ;; . python-font-lock-syntactic-face-function)
+ ))
+ (set (make-local-variable 'syntax-propertize-function)
+ python-syntax-propertize-function)
(set (make-local-variable 'parse-sexp-lookup-properties) t)
(set (make-local-variable 'parse-sexp-ignore-comments) t)
(set (make-local-variable 'comment-start) "# ")
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 0b92234bf1c..4d015de5198 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -100,17 +100,10 @@
(defconst ruby-block-end-re "\\<end\\>")
-(defconst ruby-here-doc-beg-re
+(eval-and-compile
+ (defconst ruby-here-doc-beg-re
"\\(<\\)<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)"
- "Regexp to match the beginning of a heredoc.")
-
-(defconst ruby-here-doc-end-re
- "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$"
- "Regexp to match the end of heredocs.
-
-This will actually match any line with one or more characters.
-It's useful in that it divides up the match string so that
-`ruby-here-doc-beg-match' can search for the beginning of the heredoc.")
+ "Regexp to match the beginning of a heredoc."))
(defun ruby-here-doc-end-match ()
"Return a regexp to find the end of a heredoc.
@@ -123,18 +116,6 @@ This should only be called after matching against `ruby-here-doc-beg-re'."
(match-string 5)
(match-string 6)))))
-(defun ruby-here-doc-beg-match ()
- "Return a regexp to find the beginning of a heredoc.
-
-This should only be called after matching against `ruby-here-doc-end-re'."
- (let ((contents (regexp-quote (concat (match-string 2) (match-string 3)))))
- (concat "<<"
- (let ((match (match-string 1)))
- (if (and match (> (length match) 0))
- (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)"
- contents "\\b\\(\\1\\|\\2\\)")
- (concat "-?\\([\"']\\|\\)" contents "\\b\\1"))))))
-
(defconst ruby-delimiter
(concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\("
ruby-block-beg-re
@@ -362,7 +343,7 @@ Also ignores spaces after parenthesis when 'space."
(back-to-indentation)
(current-column)))
-(defun ruby-indent-line (&optional flag)
+(defun ruby-indent-line (&optional ignored)
"Correct the indentation of the current Ruby line."
(interactive)
(ruby-indent-to (ruby-calculate-indent)))
@@ -405,8 +386,7 @@ and `\\' when preceded by `?'."
"TODO: document."
(save-excursion
(store-match-data nil)
- (let ((space (skip-chars-backward " \t"))
- (start (point)))
+ (let ((space (skip-chars-backward " \t")))
(cond
((bolp) t)
((progn
@@ -700,7 +680,7 @@ and `\\' when preceded by `?'."
(beginning-of-line)
(let ((ruby-indent-point (point))
(case-fold-search nil)
- state bol eol begin op-end
+ state eol begin op-end
(paren (progn (skip-syntax-forward " ")
(and (char-after) (matching-paren (char-after)))))
(indent 0))
@@ -780,7 +760,6 @@ and `\\' when preceded by `?'."
(if (re-search-forward "^\\s *#" end t)
(beginning-of-line)
(setq done t))))
- (setq bol (point))
(end-of-line)
;; skip the comment at the end
(skip-chars-backward " \t")
@@ -1037,10 +1016,8 @@ With ARG, do it many times. Negative ARG means move forward."
(ruby-beginning-of-defun)
(re-search-backward "^\n" (- (point) 1) t))
-(defun ruby-indent-exp (&optional shutup-p)
- "Indent each line in the balanced expression following the point.
-If a prefix arg is given or SHUTUP-P is non-nil, no errors
-are signalled if a balanced expression isn't found."
+(defun ruby-indent-exp (&optional ignored)
+ "Indent each line in the balanced expression following the point."
(interactive "*P")
(let ((here (point-marker)) start top column (nest t))
(set-marker-insertion-type here t)
@@ -1133,58 +1110,208 @@ See `add-log-current-defun-function'."
(if mlist (concat mlist mname) mname)
mlist)))))
-(defconst ruby-font-lock-syntactic-keywords
- `(;; #{ }, #$hoge, #@foo are not comments
- ("\\(#\\)[{$@]" 1 (1 . nil))
- ;; the last $', $", $` in the respective string is not variable
- ;; the last ?', ?", ?` in the respective string is not ascii code
- ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
- (2 (7 . nil))
- (4 (7 . nil)))
- ;; $' $" $` .... are variables
- ;; ?' ?" ?` are ascii codes
- ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
- ;; regexps
- ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
- (4 (7 . ?/))
- (6 (7 . ?/)))
- ("^=en\\(d\\)\\_>" 1 "!")
- ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax))
- ;; Currently, the following case is highlighted incorrectly:
- ;;
- ;; <<FOO
- ;; FOO
- ;; <<BAR
- ;; <<BAZ
- ;; BAZ
- ;; BAR
- ;;
- ;; This is because all here-doc beginnings are highlighted before any endings,
- ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ
- ;; it thinks <<BAR is part of a string so it's marked as well.
- ;;
- ;; This may be fixable by modifying ruby-in-here-doc-p to use
- ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context,
- ;; but I don't want to try that until we've got unit tests set up
- ;; to make sure I don't break anything else.
- (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
- ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
- (ruby-here-doc-beg-syntax))
- (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))
- "Syntactic keywords for Ruby mode. See `font-lock-syntactic-keywords'.")
-
-(defun ruby-comment-beg-syntax ()
- "Return the syntax cell for a the first character of a =begin.
+(if (eval-when-compile (fboundp #'syntax-propertize-rules))
+ ;; New code that works independently from font-lock.
+ (progn
+ (defun ruby-syntax-propertize-function (start end)
+ "Syntactic keywords for Ruby mode. See `syntax-propertize-function'."
+ (goto-char start)
+ (ruby-syntax-propertize-heredoc end)
+ (funcall
+ (syntax-propertize-rules
+ ;; #{ }, #$hoge, #@foo are not comments
+ ("\\(#\\)[{$@]" (1 "."))
+ ;; the last $', $", $` in the respective string is not variable
+ ;; the last ?', ?", ?` in the respective string is not ascii code
+ ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
+ (2 "\"")
+ (4 "\""))
+ ;; $' $" $` .... are variables
+ ;; ?' ?" ?` are ascii codes
+ ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" (3 "."))
+ ;; regexps
+ ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
+ (4 "\"/")
+ (6 "\"/"))
+ ("^=en\\(d\\)\\_>" (1 "!"))
+ ("^\\(=\\)begin\\_>" (1 "!"))
+ ;; Handle here documents.
+ ((concat ruby-here-doc-beg-re ".*\\(\n\\)")
+ (7 (prog1 "\"" (ruby-syntax-propertize-heredoc end)))))
+ (point) end))
+
+ (defun ruby-syntax-propertize-heredoc (limit)
+ (let ((ppss (syntax-ppss))
+ (res '()))
+ (when (eq ?\n (nth 3 ppss))
+ (save-excursion
+ (goto-char (nth 8 ppss))
+ (beginning-of-line)
+ (while (re-search-forward ruby-here-doc-beg-re
+ (line-end-position) t)
+ (push (concat (ruby-here-doc-end-match) "\n") res)))
+ (let ((start (point)))
+ ;; With multiple openers on the same line, we don't know in which
+ ;; part `start' is, so we have to go back to the beginning.
+ (when (cdr res)
+ (goto-char (nth 8 ppss))
+ (setq res (nreverse res)))
+ (while (and res (re-search-forward (pop res) limit 'move))
+ (if (null res)
+ (put-text-property (1- (point)) (point)
+ 'syntax-table (string-to-syntax "\""))))
+ ;; Make extra sure we don't move back, lest we could fall into an
+ ;; inf-loop.
+ (if (< (point) start) (goto-char start))))))
+ )
+
+ ;; For Emacsen where syntax-propertize-rules is not (yet) available,
+ ;; fallback on the old font-lock-syntactic-keywords stuff.
+
+ (defconst ruby-here-doc-end-re
+ "^\\([ \t]+\\)?\\(.*\\)\\(\n\\)"
+ "Regexp to match the end of heredocs.
+
+This will actually match any line with one or more characters.
+It's useful in that it divides up the match string so that
+`ruby-here-doc-beg-match' can search for the beginning of the heredoc.")
+
+ (defun ruby-here-doc-beg-match ()
+ "Return a regexp to find the beginning of a heredoc.
+
+This should only be called after matching against `ruby-here-doc-end-re'."
+ (let ((contents (regexp-quote (match-string 2))))
+ (concat "<<"
+ (let ((match (match-string 1)))
+ (if (and match (> (length match) 0))
+ (concat "\\(?:-\\([\"']?\\)\\|\\([\"']\\)" match "\\)"
+ contents "\\b\\(\\1\\|\\2\\)")
+ (concat "-?\\([\"']\\|\\)" contents "\\b\\1"))))))
+
+ (defconst ruby-font-lock-syntactic-keywords
+ `( ;; #{ }, #$hoge, #@foo are not comments
+ ("\\(#\\)[{$@]" 1 (1 . nil))
+ ;; the last $', $", $` in the respective string is not variable
+ ;; the last ?', ?", ?` in the respective string is not ascii code
+ ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)"
+ (2 (7 . nil))
+ (4 (7 . nil)))
+ ;; $' $" $` .... are variables
+ ;; ?' ?" ?` are ascii codes
+ ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
+ ;; regexps
+ ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
+ (4 (7 . ?/))
+ (6 (7 . ?/)))
+ ("^=en\\(d\\)\\_>" 1 "!")
+ ("^\\(=\\)begin\\_>" 1 (ruby-comment-beg-syntax))
+ ;; Currently, the following case is highlighted incorrectly:
+ ;;
+ ;; <<FOO
+ ;; FOO
+ ;; <<BAR
+ ;; <<BAZ
+ ;; BAZ
+ ;; BAR
+ ;;
+ ;; This is because all here-doc beginnings are highlighted before any endings,
+ ;; so although <<BAR is properly marked as a beginning, when we get to <<BAZ
+ ;; it thinks <<BAR is part of a string so it's marked as well.
+ ;;
+ ;; This may be fixable by modifying ruby-in-here-doc-p to use
+ ;; ruby-in-non-here-doc-string-p rather than syntax-ppss-context,
+ ;; but I don't want to try that until we've got unit tests set up
+ ;; to make sure I don't break anything else.
+ (,(concat ruby-here-doc-beg-re ".*\\(\n\\)")
+ ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re))
+ (ruby-here-doc-beg-syntax))
+ (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))
+ "Syntactic keywords for Ruby mode. See `font-lock-syntactic-keywords'.")
+
+ (defun ruby-comment-beg-syntax ()
+ "Return the syntax cell for a the first character of a =begin.
See the definition of `ruby-font-lock-syntactic-keywords'.
This returns a comment-delimiter cell as long as the =begin
isn't in a string or another comment."
- (when (not (nth 3 (syntax-ppss)))
- (string-to-syntax "!")))
+ (when (not (nth 3 (syntax-ppss)))
+ (string-to-syntax "!")))
+
+ (defun ruby-in-here-doc-p ()
+ "Return whether or not the point is in a heredoc."
+ (save-excursion
+ (let ((old-point (point)) (case-fold-search nil))
+ (beginning-of-line)
+ (catch 'found-beg
+ (while (re-search-backward ruby-here-doc-beg-re nil t)
+ (if (not (or (ruby-in-ppss-context-p 'anything)
+ (ruby-here-doc-find-end old-point)))
+ (throw 'found-beg t)))))))
+
+ (defun ruby-here-doc-find-end (&optional limit)
+ "Expects the point to be on a line with one or more heredoc openers.
+Returns the buffer position at which all heredocs on the line
+are terminated, or nil if they aren't terminated before the
+buffer position `limit' or the end of the buffer."
+ (save-excursion
+ (beginning-of-line)
+ (catch 'done
+ (let ((eol (save-excursion (end-of-line) (point)))
+ (case-fold-search nil)
+ ;; Fake match data such that (match-end 0) is at eol
+ (end-match-data (progn (looking-at ".*$") (match-data)))
+ beg-match-data end-re)
+ (while (re-search-forward ruby-here-doc-beg-re eol t)
+ (setq beg-match-data (match-data))
+ (setq end-re (ruby-here-doc-end-match))
+
+ (set-match-data end-match-data)
+ (goto-char (match-end 0))
+ (unless (re-search-forward end-re limit t) (throw 'done nil))
+ (setq end-match-data (match-data))
+
+ (set-match-data beg-match-data)
+ (goto-char (match-end 0)))
+ (set-match-data end-match-data)
+ (goto-char (match-end 0))
+ (point)))))
+
+ (defun ruby-here-doc-beg-syntax ()
+ "Return the syntax cell for a line that may begin a heredoc.
+See the definition of `ruby-font-lock-syntactic-keywords'.
+
+This sets the syntax cell for the newline ending the line
+containing the heredoc beginning so that cases where multiple
+heredocs are started on one line are handled correctly."
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (unless (or (ruby-in-ppss-context-p 'non-heredoc)
+ (ruby-in-here-doc-p))
+ (string-to-syntax "\""))))
+
+ (defun ruby-here-doc-end-syntax ()
+ "Return the syntax cell for a line that may end a heredoc.
+See the definition of `ruby-font-lock-syntactic-keywords'."
+ (let ((pss (syntax-ppss)) (case-fold-search nil))
+ ;; If we aren't in a string, we definitely aren't ending a heredoc,
+ ;; so we can just give up.
+ ;; This means we aren't doing a full-document search
+ ;; every time we enter a character.
+ (when (ruby-in-ppss-context-p 'heredoc pss)
+ (save-excursion
+ (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
+ (let ((eol (point)))
+ (beginning-of-line)
+ (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
+ (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
+ (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
+ (not (re-search-forward ruby-here-doc-beg-re eol t))))
+ (string-to-syntax "\"")))))))
-(unless (functionp 'syntax-ppss)
- (defun syntax-ppss (&optional pos)
- (parse-partial-sexp (point-min) (or pos (point)))))
+ (unless (functionp 'syntax-ppss)
+ (defun syntax-ppss (&optional pos)
+ (parse-partial-sexp (point-min) (or pos (point)))))
+ )
(defun ruby-in-ppss-context-p (context &optional ppss)
(let ((ppss (or ppss (syntax-ppss (point)))))
@@ -1195,10 +1322,7 @@ isn't in a string or another comment."
((eq context 'string)
(nth 3 ppss))
((eq context 'heredoc)
- (and (nth 3 ppss)
- ;; If it's generic string, it's a heredoc and we don't care
- ;; See `parse-partial-sexp'
- (not (numberp (nth 3 ppss)))))
+ (eq ?\n (nth 3 ppss)))
((eq context 'non-heredoc)
(and (ruby-in-ppss-context-p 'anything)
(not (ruby-in-ppss-context-p 'heredoc))))
@@ -1210,77 +1334,6 @@ isn't in a string or another comment."
"context name `" (symbol-name context) "' is unknown"))))
t)))
-(defun ruby-in-here-doc-p ()
- "Return whether or not the point is in a heredoc."
- (save-excursion
- (let ((old-point (point)) (case-fold-search nil))
- (beginning-of-line)
- (catch 'found-beg
- (while (re-search-backward ruby-here-doc-beg-re nil t)
- (if (not (or (ruby-in-ppss-context-p 'anything)
- (ruby-here-doc-find-end old-point)))
- (throw 'found-beg t)))))))
-
-(defun ruby-here-doc-find-end (&optional limit)
- "Expects the point to be on a line with one or more heredoc openers.
-Returns the buffer position at which all heredocs on the line
-are terminated, or nil if they aren't terminated before the
-buffer position `limit' or the end of the buffer."
- (save-excursion
- (beginning-of-line)
- (catch 'done
- (let ((eol (save-excursion (end-of-line) (point)))
- (case-fold-search nil)
- ;; Fake match data such that (match-end 0) is at eol
- (end-match-data (progn (looking-at ".*$") (match-data)))
- beg-match-data end-re)
- (while (re-search-forward ruby-here-doc-beg-re eol t)
- (setq beg-match-data (match-data))
- (setq end-re (ruby-here-doc-end-match))
-
- (set-match-data end-match-data)
- (goto-char (match-end 0))
- (unless (re-search-forward end-re limit t) (throw 'done nil))
- (setq end-match-data (match-data))
-
- (set-match-data beg-match-data)
- (goto-char (match-end 0)))
- (set-match-data end-match-data)
- (goto-char (match-end 0))
- (point)))))
-
-(defun ruby-here-doc-beg-syntax ()
- "Return the syntax cell for a line that may begin a heredoc.
-See the definition of `ruby-font-lock-syntactic-keywords'.
-
-This sets the syntax cell for the newline ending the line
-containing the heredoc beginning so that cases where multiple
-heredocs are started on one line are handled correctly."
- (save-excursion
- (goto-char (match-beginning 0))
- (unless (or (ruby-in-ppss-context-p 'non-heredoc)
- (ruby-in-here-doc-p))
- (string-to-syntax "|"))))
-
-(defun ruby-here-doc-end-syntax ()
- "Return the syntax cell for a line that may end a heredoc.
-See the definition of `ruby-font-lock-syntactic-keywords'."
- (let ((pss (syntax-ppss)) (case-fold-search nil))
- ;; If we aren't in a string, we definitely aren't ending a heredoc,
- ;; so we can just give up.
- ;; This means we aren't doing a full-document search
- ;; every time we enter a character.
- (when (ruby-in-ppss-context-p 'heredoc pss)
- (save-excursion
- (goto-char (nth 8 pss)) ; Go to the beginning of heredoc.
- (let ((eol (point)))
- (beginning-of-line)
- (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line...
- (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment...
- (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line...
- (not (re-search-forward ruby-here-doc-beg-re eol t))))
- (string-to-syntax "|")))))))
-
(if (featurep 'xemacs)
(put 'ruby-mode 'font-lock-defaults
'((ruby-font-lock-keywords)
@@ -1377,8 +1430,10 @@ See `font-lock-syntax-table'.")
)
"Additional expressions to highlight in Ruby mode.")
+(defvar electric-indent-chars)
+
;;;###autoload
-(defun ruby-mode ()
+(define-derived-mode ruby-mode prog-mode "Ruby"
"Major mode for editing Ruby scripts.
\\[ruby-indent-line] properly indents subexpressions of multi-line
class, module, def, if, while, for, do, and case statements, taking
@@ -1387,27 +1442,22 @@ nesting into account.
The variable `ruby-indent-level' controls the amount of indentation.
\\{ruby-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map ruby-mode-map)
- (setq mode-name "Ruby")
- (setq major-mode 'ruby-mode)
(ruby-mode-variables)
- (set (make-local-variable 'indent-line-function)
- 'ruby-indent-line)
(set (make-local-variable 'imenu-create-index-function)
'ruby-imenu-create-index)
(set (make-local-variable 'add-log-current-defun-function)
'ruby-add-log-current-method)
(add-hook
- (cond ((boundp 'before-save-hook)
- (make-local-variable 'before-save-hook)
- 'before-save-hook)
+ (cond ((boundp 'before-save-hook) 'before-save-hook)
((boundp 'write-contents-functions) 'write-contents-functions)
((boundp 'write-contents-hooks) 'write-contents-hooks))
- 'ruby-mode-set-encoding)
+ 'ruby-mode-set-encoding nil 'local)
+
+ (set (make-local-variable 'electric-indent-chars)
+ (append '(?\{ ?\}) (if (boundp 'electric-indent-chars)
+ (default-value 'electric-indent-chars))))
(set (make-local-variable 'font-lock-defaults)
'((ruby-font-lock-keywords) nil nil))
@@ -1415,12 +1465,12 @@ The variable `ruby-indent-level' controls the amount of indentation.
ruby-font-lock-keywords)
(set (make-local-variable 'font-lock-syntax-table)
ruby-font-lock-syntax-table)
- (set (make-local-variable 'font-lock-syntactic-keywords)
- ruby-font-lock-syntactic-keywords)
- (if (fboundp 'run-mode-hooks)
- (run-mode-hooks 'ruby-mode-hook)
- (run-hooks 'ruby-mode-hook)))
+ (if (eval-when-compile (fboundp 'syntax-propertize-rules))
+ (set (make-local-variable 'syntax-propertize-function)
+ #'ruby-syntax-propertize-function)
+ (set (make-local-variable 'font-lock-syntactic-keywords)
+ ruby-font-lock-syntactic-keywords)))
;;; Invoke ruby-mode when appropriate
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 9041bd50259..d41a81e38a6 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -939,7 +939,6 @@ See `sh-feature'.")
;; These are used for the syntax table stuff (derived from cperl-mode).
;; Note: parse-sexp-lookup-properties must be set to t for it to work.
(defconst sh-st-punc (string-to-syntax "."))
-(defconst sh-st-symbol (string-to-syntax "_"))
(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
(defconst sh-escaped-line-re
@@ -957,7 +956,7 @@ See `sh-feature'.")
(defvar sh-here-doc-re sh-here-doc-open-re)
(make-variable-buffer-local 'sh-here-doc-re)
-(defun sh-font-lock-close-heredoc (bol eof indented)
+(defun sh-font-lock-close-heredoc (bol eof indented eol)
"Determine the syntax of the \\n after an EOF.
If non-nil INDENTED indicates that the EOF was indented."
(let* ((eof-re (if eof (regexp-quote eof) ""))
@@ -971,6 +970,8 @@ If non-nil INDENTED indicates that the EOF was indented."
(ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
(start (save-excursion
(goto-char bol)
+ ;; FIXME: will incorrectly find a <<EOF embedded inside
+ ;; the heredoc.
(re-search-backward (concat sre "\\|" ere) nil t))))
;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
;; found a close-heredoc which makes the current close-heredoc inoperant.
@@ -990,7 +991,7 @@ If non-nil INDENTED indicates that the EOF was indented."
(sh-in-comment-or-string (point)))))
;; No <<EOF2 found after our <<.
(= (point) start)))
- sh-here-doc-syntax)
+ (put-text-property eol (1+ eol) 'syntax-table sh-here-doc-syntax))
((not (or start (save-excursion (re-search-forward sre nil t))))
;; There's no <<EOF either before or after us,
;; so we should remove ourselves from font-lock's keywords.
@@ -1000,7 +1001,7 @@ If non-nil INDENTED indicates that the EOF was indented."
(regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
nil))))
-(defun sh-font-lock-open-heredoc (start string)
+(defun sh-font-lock-open-heredoc (start string eol)
"Determine the syntax of the \\n after a <<EOF.
START is the position of <<.
STRING is the actual word used as delimiter (e.g. \"EOF\").
@@ -1030,13 +1031,8 @@ Point is at the beginning of the next line."
;; Don't bother fixing it now, but place a multiline property so
;; that when jit-lock-context-* refontifies the rest of the
;; buffer, it also refontifies the current line with it.
- (put-text-property start (point) 'font-lock-multiline t)))
- sh-here-doc-syntax))
-
-(defun sh-font-lock-here-doc (limit)
- "Search for a heredoc marker."
- ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
- (re-search-forward sh-here-doc-re limit t))
+ (put-text-property start (point) 'syntax-multiline t)))
+ (put-text-property eol (1+ eol) 'syntax-table sh-here-doc-syntax)))
(defun sh-font-lock-quoted-subshell (limit)
"Search for a subshell embedded in a string.
@@ -1045,9 +1041,7 @@ subshells can nest."
;; FIXME: This can (and often does) match multiple lines, yet it makes no
;; effort to handle multiline cases correctly, so it ends up being
;; rather flakey.
- (when (and (re-search-forward "\"\\(?:\\(?:.\\|\n\\)*?[^\\]\\(?:\\\\\\\\\\)*\\)??\\(\\$(\\|`\\)" limit t)
- ;; Make sure the " we matched is an opening quote.
- (eq ?\" (nth 3 (syntax-ppss))))
+ (when (eq ?\" (nth 3 (syntax-ppss))) ; Check we matched an opening quote.
;; bingo we have a $( or a ` inside a ""
(let ((char (char-after (point)))
;; `state' can be: double-quote, backquote, code.
@@ -1082,8 +1076,7 @@ subshells can nest."
(double-quote nil)
(t (setq state (pop states)))))
(t (error "Internal error in sh-font-lock-quoted-subshell")))
- (forward-char 1)))
- t))
+ (forward-char 1)))))
(defun sh-is-quoted-p (pos)
@@ -1122,7 +1115,7 @@ subshells can nest."
(when (progn (backward-char 2)
(if (> start (line-end-position))
(put-text-property (point) (1+ start)
- 'font-lock-multiline t))
+ 'syntax-multiline t))
;; FIXME: The `in' may just be a random argument to
;; a normal command rather than the real `in' keyword.
;; I.e. we should look back to try and find the
@@ -1136,40 +1129,44 @@ subshells can nest."
sh-st-punc
nil))
-(defun sh-font-lock-flush-syntax-ppss-cache (limit)
- ;; This should probably be a standard function provided by font-lock.el
- ;; (or syntax.el).
- (syntax-ppss-flush-cache (point))
- (goto-char limit)
- nil)
-
-(defconst sh-font-lock-syntactic-keywords
- ;; A `#' begins a comment when it is unquoted and at the beginning of a
- ;; word. In the shell, words are separated by metacharacters.
- ;; The list of special chars is taken from the single-unix spec
- ;; of the shell command language (under `quoting') but with `$' removed.
- `(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 ,sh-st-symbol)
- ;; In a '...' the backslash is not escaping.
- ("\\(\\\\\\)'" (1 (sh-font-lock-backslash-quote)))
- ;; The previous rule uses syntax-ppss, but the subsequent rules may
- ;; change the syntax, so we have to tell syntax-ppss that the states it
- ;; has just computed will need to be recomputed.
- (sh-font-lock-flush-syntax-ppss-cache)
- ;; Make sure $@ and $? are correctly recognized as sexps.
- ("\\$\\([?@]\\)" 1 ,sh-st-symbol)
- ;; Find HEREDOC starters and add a corresponding rule for the ender.
- (sh-font-lock-here-doc
- (2 (sh-font-lock-open-heredoc
- (match-beginning 0) (match-string 1)) nil t)
- (5 (sh-font-lock-close-heredoc
- (match-beginning 0) (match-string 4)
- (and (match-beginning 3) (/= (match-beginning 3) (match-end 3))))
- nil t))
- ;; Distinguish the special close-paren in `case'.
- (")" 0 (sh-font-lock-paren (match-beginning 0)))
- ;; highlight (possibly nested) subshells inside "" quoted regions correctly.
- ;; This should be at the very end because it uses syntax-ppss.
- (sh-font-lock-quoted-subshell)))
+(defun sh-syntax-propertize-function (start end)
+ (goto-char start)
+ (while (prog1
+ (re-search-forward sh-here-doc-re end 'move)
+ (save-excursion
+ (save-match-data
+ (funcall
+ (syntax-propertize-rules
+ ;; A `#' begins a comment when it is unquoted and at the
+ ;; beginning of a word. In the shell, words are separated by
+ ;; metacharacters. The list of special chars is taken from
+ ;; the single-unix spec of the shell command language (under
+ ;; `quoting') but with `$' removed.
+ ("[^|&;<>()`\\\"' \t\n]\\(#+\\)" (1 "_"))
+ ;; In a '...' the backslash is not escaping.
+ ("\\(\\\\\\)'" (1 (sh-font-lock-backslash-quote)))
+ ;; Make sure $@ and $? are correctly recognized as sexps.
+ ("\\$\\([?@]\\)" (1 "_"))
+ ;; Distinguish the special close-paren in `case'.
+ (")" (0 (sh-font-lock-paren (match-beginning 0))))
+ ;; Highlight (possibly nested) subshells inside "" quoted
+ ;; regions correctly.
+ ("\"\\(?:\\(?:.\\|\n\\)*?[^\\]\\(?:\\\\\\\\\\)*\\)??\\(\\$(\\|`\\)"
+ (1 (ignore
+ ;; Save excursion because we want to also apply other
+ ;; syntax-propertize rules within the affected region.
+ (save-excursion
+ (sh-font-lock-quoted-subshell end))))))
+ (prog1 start (setq start (point))) (point)))))
+ (if (match-beginning 2)
+ ;; FIXME: actually, once we see an heredoc opener, we should just
+ ;; search for its ender without propertizing anything in it.
+ (sh-font-lock-open-heredoc
+ (match-beginning 0) (match-string 1) (match-beginning 2))
+ (sh-font-lock-close-heredoc
+ (match-beginning 0) (match-string 4)
+ (and (match-beginning 3) (/= (match-beginning 3) (match-end 3)))
+ (match-beginning 5)))))
(defun sh-font-lock-syntactic-face-function (state)
(let ((q (nth 3 state)))
@@ -1553,9 +1550,12 @@ with your script for an edit-interpret-debug cycle."
sh-font-lock-keywords-1 sh-font-lock-keywords-2)
nil nil
((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
- (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords)
(font-lock-syntactic-face-function
. sh-font-lock-syntactic-face-function)))
+ (set (make-local-variable 'syntax-propertize-function)
+ #'sh-syntax-propertize-function)
+ (add-hook 'syntax-propertize-extend-region-functions
+ #'syntax-propertize-multiline 'append 'local)
(set (make-local-variable 'skeleton-pair-alist) '((?` _ ?`)))
(set (make-local-variable 'skeleton-pair-filter-function) 'sh-quoted-p)
(set (make-local-variable 'skeleton-further-elements)
diff --git a/lisp/progmodes/simula.el b/lisp/progmodes/simula.el
index f8d1a6aca97..34c50b6cfe5 100644
--- a/lisp/progmodes/simula.el
+++ b/lisp/progmodes/simula.el
@@ -163,17 +163,18 @@ for SIMULA mode to function correctly."
(defvar simula-mode-syntax-table nil
"Syntax table in SIMULA mode buffers.")
-(defconst simula-font-lock-syntactic-keywords
- `(;; `comment' directive.
- ("\\<\\(c\\)omment\\>" 1 "<")
- ;; end comments
- (,(concat "\\<end\\>\\([^;\n]\\).*?\\(\n\\|\\(.\\)\\(;\\|"
- (regexp-opt '("end" "else" "when" "otherwise"))
- "\\)\\)")
- (1 "< b")
- (3 "> b" nil t))
- ;; non-quoted single-quote char.
- ("'\\('\\)'" 1 ".")))
+(defconst simula-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; `comment' directive.
+ ("\\<\\(c\\)omment\\>" (1 "<"))
+ ;; end comments
+ ((concat "\\<end\\>\\([^;\n]\\).*?\\(\n\\|\\(.\\)\\(;\\|"
+ (regexp-opt '("end" "else" "when" "otherwise"))
+ "\\)\\)")
+ (1 "< b")
+ (3 "> b"))
+ ;; non-quoted single-quote char.
+ ("'\\('\\)'" (1 "."))))
;; Regexps written with help from Alf-Ivar Holm <alfh@ifi.uio.no>.
(defconst simula-font-lock-keywords-1
@@ -396,8 +397,9 @@ with no arguments, if that value is non-nil."
(setq font-lock-defaults
'((simula-font-lock-keywords simula-font-lock-keywords-1
simula-font-lock-keywords-2 simula-font-lock-keywords-3)
- nil t ((?_ . "w")) nil
- (font-lock-syntactic-keywords . simula-font-lock-syntactic-keywords)))
+ nil t ((?_ . "w"))))
+ (set (make-local-variable 'syntax-propertize-function)
+ simula-syntax-propertize-function)
(abbrev-mode 1))
(defun simula-indent-exp ()
diff --git a/lisp/progmodes/tcl.el b/lisp/progmodes/tcl.el
index 29096a23046..8f80d13bab6 100644
--- a/lisp/progmodes/tcl.el
+++ b/lisp/progmodes/tcl.el
@@ -411,9 +411,10 @@ This variable is generally set from `tcl-proc-regexp',
`tcl-typeword-list', and `tcl-keyword-list' by the function
`tcl-set-font-lock-keywords'.")
-(defvar tcl-font-lock-syntactic-keywords
- ;; Mark the few `#' that are not comment-markers.
- '(("[^;[{ \t\n][ \t]*\\(#\\)" (1 ".")))
+(defconst tcl-syntax-propertize-function
+ (syntax-propertize-rules
+ ;; Mark the few `#' that are not comment-markers.
+ ("[^;[{ \t\n][ \t]*\\(#\\)" (1 ".")))
"Syntactic keywords for `tcl-mode'.")
;; FIXME need some way to recognize variables because array refs look
@@ -593,9 +594,9 @@ Commands:
(set (make-local-variable 'outline-level) 'tcl-outline-level)
(set (make-local-variable 'font-lock-defaults)
- '(tcl-font-lock-keywords nil nil nil beginning-of-defun
- (font-lock-syntactic-keywords . tcl-font-lock-syntactic-keywords)
- (parse-sexp-lookup-properties . t)))
+ '(tcl-font-lock-keywords nil nil nil beginning-of-defun))
+ (set (make-local-variable 'syntax-propertize-function)
+ tcl-syntax-propertize-function)
(set (make-local-variable 'imenu-generic-expression)
tcl-imenu-generic-expression)
diff --git a/lisp/progmodes/vhdl-mode.el b/lisp/progmodes/vhdl-mode.el
index 4ff9cf92b8d..24768d93e6a 100644
--- a/lisp/progmodes/vhdl-mode.el
+++ b/lisp/progmodes/vhdl-mode.el
@@ -4693,8 +4693,15 @@ Key bindings:
(set (make-local-variable 'font-lock-defaults)
(list
'(nil vhdl-font-lock-keywords) nil
- (not vhdl-highlight-case-sensitive) '((?\_ . "w")) 'beginning-of-line
- '(font-lock-syntactic-keywords . vhdl-font-lock-syntactic-keywords)))
+ (not vhdl-highlight-case-sensitive) '((?\_ . "w")) 'beginning-of-line))
+ (if (eval-when-compile (fboundp 'syntax-propertize-rules))
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-rules
+ ;; Mark single quotes as having string quote syntax in
+ ;; 'c' instances.
+ ("\\(\'\\).\\(\'\\)" (1 "\"'") (2 "\"'"))))
+ (set (make-local-variable 'font-lock-syntactic-keywords)
+ vhdl-font-lock-syntactic-keywords))
(unless vhdl-emacs-21
(set (make-local-variable 'font-lock-support-mode) 'lazy-lock-mode)
(set (make-local-variable 'lazy-lock-defer-contextually) nil)
@@ -12914,10 +12921,9 @@ This does background highlighting of translate-off regions.")
"Re-initialize fontification and fontify buffer."
(interactive)
(setq font-lock-defaults
- (list
- 'vhdl-font-lock-keywords nil
- (not vhdl-highlight-case-sensitive) '((?\_ . "w")) 'beginning-of-line
- '(font-lock-syntactic-keywords . vhdl-font-lock-syntactic-keywords)))
+ `(vhdl-font-lock-keywords
+ nil ,(not vhdl-highlight-case-sensitive) ((?\_ . "w"))
+ beginning-of-line))
(when (fboundp 'font-lock-unset-defaults)
(font-lock-unset-defaults)) ; not implemented in XEmacs
(font-lock-set-defaults)