Mercurial > emacs
changeset 110403:14dab55b2888
Fix and improve last syntax-propertize patch
* lisp/emacs-lisp/syntax.el (syntax-propertize-precompile-rules): New macro.
(syntax-propertize-rules): Add var-ref case. Fix offset computation
when adding surrounding \(..\).
* lisp/progmodes/fortran.el (fortran--font-lock-syntactic-keywords): Remove.
(fortran-make-syntax-propertize-function): New function; replaces
fortran-font-lock-syntactic-keywords.
(fortran-mode): Use it.
(fortran-line-length): Use it. Improve interactive spec.
* lisp/progmodes/js.el (js-mode): Fix last change (bug#7054).
* lisp/textmodes/tex-mode.el (tex-syntax-propertize-rules)
(latex-syntax-propertize-rules): New consts; replace
tex-font-lock-syntactic-keywords.
(tex-env-mark, latex-env-before-change): New functions.
(latex-electric-env-pair-mode): New minor mode.
(tex-font-lock-verb): Change arguments; do move point.
(tex-font-lock-syntactic-face-function): Adjust to new verbatim
representation as a form of comment.
(tex-font-lock-keywords-1): Remove workaround, now unneeded.
(doctex-syntax-propertize-rules): New const; replaces
doctex-font-lock-syntactic-keywords.
(tex-common-initialization, doctex-mode): Use syntax-propertize-rules.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Sat, 18 Sep 2010 02:35:00 +0200 |
parents | c848ff4a679b |
children | afd1f9799297 |
files | etc/NEWS lisp/ChangeLog lisp/emacs-lisp/syntax.el lisp/progmodes/fortran.el lisp/progmodes/js.el lisp/textmodes/tex-mode.el |
diffstat | 6 files changed, 220 insertions(+), 97 deletions(-) [+] |
line wrap: on
line diff
--- a/etc/NEWS Sat Sep 18 00:28:10 2010 +0200 +++ b/etc/NEWS Sat Sep 18 02:35:00 2010 +0200 @@ -236,6 +236,8 @@ * Changes in Specialized Modes and Packages in Emacs 24.1 +** latex-electric-env-pair-mode keeps \begin..\end matched on the fly. + ** FIXME: xdg-open for browse-url and reportbug, 2010/08. (Close bug#4546?) ** Archive Mode has basic support to browse 7z archives.
--- a/lisp/ChangeLog Sat Sep 18 00:28:10 2010 +0200 +++ b/lisp/ChangeLog Sat Sep 18 02:35:00 2010 +0200 @@ -1,3 +1,30 @@ +2010-09-18 Stefan Monnier <monnier@iro.umontreal.ca> + + * textmodes/tex-mode.el (tex-syntax-propertize-rules) + (latex-syntax-propertize-rules): New consts; replace + tex-font-lock-syntactic-keywords. + (tex-env-mark, latex-env-before-change): New functions. + (latex-electric-env-pair-mode): New minor mode. + (tex-font-lock-verb): Change arguments; do move point. + (tex-font-lock-syntactic-face-function): Adjust to new verbatim + representation as a form of comment. + (tex-font-lock-keywords-1): Remove workaround, now unneeded. + (doctex-syntax-propertize-rules): New const; replaces + doctex-font-lock-syntactic-keywords. + (tex-common-initialization, doctex-mode): Use syntax-propertize-rules. + + * progmodes/fortran.el (fortran--font-lock-syntactic-keywords): Remove. + (fortran-make-syntax-propertize-function): New function; replaces + fortran-font-lock-syntactic-keywords. + (fortran-mode): Use it. + (fortran-line-length): Use it. Improve interactive spec. + + * emacs-lisp/syntax.el (syntax-propertize-precompile-rules): New macro. + (syntax-propertize-rules): Add var-ref case. Fix offset computation + when adding surrounding \(..\). + + * progmodes/js.el (js-mode): Fix last change (bug#7054). + 2010-09-17 Stefan Monnier <monnier@iro.umontreal.ca> * obsolete/old-whitespace.el (whitespace-rescan-files-in-buffers):
--- a/lisp/emacs-lisp/syntax.el Sat Sep 18 00:28:10 2010 +0200 +++ b/lisp/emacs-lisp/syntax.el Sat Sep 18 02:35:00 2010 +0200 @@ -57,7 +57,11 @@ ;; syntax-ppss-flush-cache since that would not only flush the cache but also ;; reset syntax-propertize--done which should not be done in this case). "Mode-specific function to apply the syntax-table properties. -Called with 2 arguments: START and END.") +Called with 2 arguments: START and END. +This function can call `syntax-ppss' on any position before END, but it +should not call `syntax-ppss-flush-cache', which means that it should not +call `syntax-ppss' on some position and later modify the buffer on some +earlier position.") (defvar syntax-propertize-chunk-size 500) @@ -109,15 +113,35 @@ t t s 1)) re t t)) +(defmacro syntax-propertize-precompile-rules (&rest rules) + "Return a precompiled form of RULES to pass to `syntax-propertize-rules'. +The arg RULES can be of the same form as in `syntax-propertize-rules'. +The return value is an object that can be passed as a rule to +`syntax-propertize-rules'. +I.e. this is useful only when you want to share rules among several +syntax-propertize-functions." + (declare (debug syntax-propertize-rules)) + ;; Precompile? Yeah, right! + ;; Seriously, tho, this is a macro for 2 reasons: + ;; - we could indeed do some pre-compilation at some point in the future, + ;; e.g. fi/when we switch to a DFA-based implementation of + ;; syntax-propertize-rules. + ;; - this lets Edebug properly annotate the expressions inside RULES. + `',rules) + (defmacro syntax-propertize-rules (&rest rules) "Make a function that applies RULES for use in `syntax-propertize-function'. The function will scan the buffer, applying the rules where they match. The buffer is scanned a single time, like \"lex\" would, rather than once per rule. -Each rule has the form (REGEXP HIGHLIGHT1 ... HIGHLIGHTn), where REGEXP -is an expression (evaluated at time of macro-expansion) that returns a regexp, -and where HIGHLIGHTs have the form (NUMBER SYNTAX) which means to +Each RULE can be a symbol, in which case that symbol's value should be, +at macro-expansion time, a precompiled set of rules, as returned +by `syntax-propertize-precompile-rules'. + +Otherwise, RULE should have the form (REGEXP HIGHLIGHT1 ... HIGHLIGHTn), where +REGEXP is an expression (evaluated at time of macro-expansion) that returns +a regexp, and where HIGHLIGHTs have the form (NUMBER SYNTAX) which means to apply the property SYNTAX to the chars matched by the subgroup NUMBER of the regular expression, if NUMBER did match. SYNTAX is an expression that returns a value to apply as `syntax-table' @@ -132,11 +156,18 @@ some parts of the text or may be applied several times to other parts. Note: back-references in REGEXPs do not work." - (declare (debug (&rest (form &rest + (declare (debug (&rest &or symbolp ;FIXME: edebug this eval step. + (form &rest (numberp - [&or stringp + [&or stringp ;FIXME: Use &wrap ("prog1" [&or stringp def-form] def-body) def-form]))))) + (let ((newrules nil)) + (while rules + (if (symbolp (car rules)) + (setq rules (append (symbol-value (pop rules)) rules)) + (push (pop rules) newrules))) + (setq rules (nreverse newrules))) (let* ((offset 0) (branches '()) ;; We'd like to use a real DFA-based lexer, usually, but since Emacs @@ -145,7 +176,8 @@ (re (mapconcat (lambda (rule) - (let ((re (eval (car rule)))) + (let* ((orig-re (eval (car rule))) + (re orig-re)) (when (and (assq 0 rule) (cdr rules)) ;; If there's more than 1 rule, and the rule want to apply ;; highlight to match 0, create an extra group to be able to @@ -229,7 +261,7 @@ code)))) (push (cons condition (nreverse code)) branches)) - (incf offset (regexp-opt-depth re)) + (incf offset (regexp-opt-depth orig-re)) re)) rules "\\|")))
--- a/lisp/progmodes/fortran.el Sat Sep 18 00:28:10 2010 +0200 +++ b/lisp/progmodes/fortran.el Sat Sep 18 02:35:00 2010 +0200 @@ -483,19 +483,27 @@ "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 ;; standard comment will throw fontification off on the wrong track. ;; So we do syntactic fontification with regexps. -(defun fortran-font-lock-syntactic-keywords () - "Return a value for `font-lock-syntactic-keywords' in Fortran mode. -This varies according to the value of `fortran-line-length'. +(defun fortran-make-syntax-propertize-function (line-length) + "Return a value for `syntax-propertize-function' in Fortran mode. +This varies according to the value of LINE-LENGTH. This is used to fontify fixed-format Fortran comments." - `(("^[cd\\*]" 0 (11)) - (,(format "^[^cd\\*\t\n].\\{%d\\}\\([^\n]+\\)" (1- fortran-line-length)) - 1 (11)))) + ;; This results in a non-byte-compiled function. We could pass it through + ;; `byte-compile', but simple benchmarks indicate that it's probably not + ;; worth the trouble (about ½% of slow down). + (eval ;I hate `eval', but it's hard to avoid it here. + `(syntax-propertize-rules + ("^[cd\\*]" (0 "<")) + ;; We mark all chars after line-length as "comment-start", rather than + ;; just the first one. This is so that a closing ' that's past the + ;; line-length will indeed be ignored (and will result in a string that + ;; leaks into subsequent lines). + ((format "^[^cd\\*\t\n].\\{%d\\}\\(.+\\)" (1- line-length)) + (1 "<"))))) (defvar fortran-font-lock-keywords fortran-font-lock-keywords-1 "Default expressions to highlight in Fortran mode.") @@ -889,10 +897,8 @@ fortran-font-lock-keywords-4) nil t ((?/ . "$/") ("_$" . "w")) 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)) + (fortran-make-syntax-propertize-function fortran-line-length)) (set (make-local-variable 'imenu-case-fold-search) t) (set (make-local-variable 'imenu-generic-expression) fortran-imenu-generic-expression) @@ -912,27 +918,30 @@ "Set the length of fixed-form Fortran lines to NCHARS. This normally only affects the current buffer, which must be in Fortran mode. If the optional argument GLOBAL is non-nil, it -affects all Fortran buffers, and also the default." - (interactive "p") - (let (new) - (mapc (lambda (buff) - (with-current-buffer buff - (when (eq major-mode 'fortran-mode) - (setq fortran-line-length nchars - fill-column fortran-line-length - new (fortran-make-syntax-propertize-function)) - ;; Refontify only if necessary. - (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)))))) +affects all Fortran buffers, and also the default. +If a numeric prefix argument is specified, it will be used as NCHARS, +otherwise is a non-numeric prefix arg is specified, the length will be +provided via the minibuffer, and otherwise the current column is used." + (interactive + (list (cond + ((numberp current-prefix-arg) current-prefix-arg) + (current-prefix-arg + (read-number "Line length: " (default-value 'fortran-line-length))) + (t (current-column))))) + (dolist (buff (if global + (buffer-list) + (list (current-buffer)))) + (with-current-buffer buff + (when (derived-mode-p 'fortran-mode) + (unless (eq fortran-line-length nchars) + (setq fortran-line-length nchars + fill-column fortran-line-length + syntax-propertize-function + (fortran-make-syntax-propertize-function nchars)) + (syntax-ppss-flush-cache (point-min)) + (if font-lock-mode (font-lock-mode 1)))))) (if global - (buffer-list) - (list (current-buffer)))) - (if global - (setq-default fortran-line-length nchars)))) + (setq-default fortran-line-length nchars))) (defun fortran-hack-local-variables () "Fortran mode adds this to `hack-local-variables-hook'."
--- a/lisp/progmodes/js.el Sat Sep 18 00:28:10 2010 +0200 +++ b/lisp/progmodes/js.el Sat Sep 18 02:35:00 2010 +0200 @@ -3304,7 +3304,7 @@ (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil) (set (make-local-variable 'font-lock-defaults) - '(js--font-lock-keywords)) + (list js--font-lock-keywords)) (set (make-local-variable 'syntax-propertize-function) js-syntax-propertize-function)
--- a/lisp/textmodes/tex-mode.el Sat Sep 18 00:28:10 2010 +0200 +++ b/lisp/textmodes/tex-mode.el Sat Sep 18 02:35:00 2010 +0200 @@ -488,10 +488,6 @@ ;; (arg "\\(?:{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)\\|\\\\[a-z*]+\\)")) (arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)")) (list - ;; tex-font-lock-syntactic-keywords causes the \ of \end{verbatim} to be - ;; highlighted as tex-verbatim face. Let's undo that. - ;; This is ugly and brittle :-( --Stef - '("^\\(\\\\\\)end" (1 (get-text-property (match-end 1) 'face) t)) ;; display $$ math $$ ;; We only mark the match between $$ and $$ because the $$ delimiters ;; themselves have already been marked (along with $..$) by syntactic @@ -642,29 +638,90 @@ (put 'tex-verbatim-environments 'safe-local-variable (lambda (x) (null (delq t (mapcar 'stringp x))))) -(defvar tex-font-lock-syntactic-keywords - '((eval . `(,(concat "^\\\\begin *{" - (regexp-opt tex-verbatim-environments t) - "}.*\\(\n\\)") 2 "|")) - ;; Technically, we'd like to put the "|" property on the \n preceding - ;; the \end, but this would have 2 disadvantages: - ;; 1 - it's wrong if the verbatim env is empty (the same \n is used to - ;; start and end the fenced-string). - ;; 2 - font-lock considers the preceding \n as being part of the - ;; preceding line, so things gets screwed every time the previous - ;; line is re-font-locked on its own. - ;; There's a hack in tex-font-lock-keywords-1 to remove the verbatim - ;; face from the \ but C-M-f still jumps to the wrong spot :-( --Stef - ;; FIXME: See gud.el for an example of a solution to a similar problem. - (eval . `(,(concat "^\\(\\\\\\)end *{" - (regexp-opt tex-verbatim-environments t) - "}\\(.?\\)") (1 "|") (3 "<"))) - ;; ("^\\(\\\\\\)begin *{comment}" 1 "< b") - ;; ("^\\\\end *{comment}.*\\(\n\\)" 1 "> b") +(eval-when-compile + (defconst tex-syntax-propertize-rules + (syntax-propertize-precompile-rules ("\\\\verb\\**\\([^a-z@*]\\)" - ;; Do it last, because it uses syntax-ppss which needs the - ;; syntax-table properties of previous entries. - 1 (tex-font-lock-verb (match-end 1))))) + (1 (prog1 "\"" + (tex-font-lock-verb + (match-beginning 0) (char-after (match-beginning 1)))))))) + + (defconst latex-syntax-propertize-rules + (syntax-propertize-precompile-rules + tex-syntax-propertize-rules + ("\\\\\\(?:end\\|begin\\) *\\({[^\n{}]*}\\)" + (1 (ignore + (tex-env-mark (match-beginning 0) + (match-beginning 1) (match-end 1)))))))) + +(defun tex-env-mark (cmd start end) + (when (= cmd (line-beginning-position)) + (let ((arg (buffer-substring-no-properties (1+ start) (1- end)))) + (when (member arg tex-verbatim-environments) + (if (eq ?b (char-after (1+ cmd))) + ;; \begin + (put-text-property (line-end-position) + (line-beginning-position 2) + 'syntax-table (string-to-syntax "< c")) + ;; In the case of an empty verbatim env, the \n after the \begin is + ;; the same as the \n before the \end. Lucky for us, the "> c" + ;; property associated to the \end will be placed afterwards, so it + ;; will override the "< c". + (put-text-property (1- cmd) cmd + 'syntax-table (string-to-syntax "> c")) + ;; The text between \end{verbatim} and \n is ignored, so we'll treat + ;; it as a comment. + (put-text-property end (min (1+ end) (line-end-position)) + 'syntax-table (string-to-syntax "<")))))) + ;; Mark env args for possible electric pairing. + (unless (get-char-property (1+ start) 'text-clones) ;Already paired-up. + (put-text-property start end 'latex-env-pair t))) + +(define-minor-mode latex-electric-env-pair-mode + "Automatically update the \\end arg when editing the \\begin one. +And vice-versa." + :lighter "/e" + (if latex-electric-env-pair-mode + (add-hook 'before-change-functions + #'latex-env-before-change nil 'local) + (remove-hook 'before-change-functions + #'latex-env-before-change 'local))) + +(defun latex-env-before-change (start end) + (when (get-text-property start 'latex-env-pair) + (condition-case err + (with-silent-modifications + ;; Remove properties even if don't find a pair. + (remove-text-properties + (previous-single-property-change (1+ start) 'latex-env-pair) + (next-single-property-change start 'latex-env-pair) + '(latex-env-pair)) + (unless (or (get-char-property start 'text-clones) + (get-char-property (1+ start) 'text-clones) + (save-excursion + (goto-char start) + (not (re-search-backward + "\\\\\\(?:end\\|begi\\(n\\)\\) *{" + (line-beginning-position) t)))) + (let ((cmd-start (match-beginning 0)) + (type (match-end 1)) ;nil for \end, else \begin. + (arg-start (1- (match-end 0)))) + (save-excursion + (goto-char (match-end 0)) + (when (and (looking-at "[^\n{}]*}") + (> (match-end 0) end)) + (let ((arg-end (match-end 0))) + (if (null type) ;\end + (progn (goto-char arg-end) + (latex-forward-sexp -1) (forward-word 1)) + (goto-char cmd-start) + (latex-forward-sexp 1) + (let (forward-sexp-function) (backward-sexp))) + (when (looking-at + (regexp-quote (buffer-substring arg-start arg-end))) + (text-clone-create arg-start arg-end)))))))) + (scan-error nil) + (error (message "Error in latex-env-before-change: %s" err))))) (defun tex-font-lock-unfontify-region (beg end) (font-lock-default-unfontify-region beg end) @@ -731,37 +788,32 @@ (define-obsolete-face-alias 'tex-verbatim-face 'tex-verbatim "22.1") (defvar tex-verbatim-face 'tex-verbatim) -(defun tex-font-lock-verb (end) - "Place syntax-table properties on the \verb construct. -END is the position of the first delimiter after \verb." - (unless (nth 8 (syntax-ppss end)) +(defun tex-font-lock-verb (start delim) + "Place syntax table properties on the \verb construct. +START is the position of the \\ and DELIM is the delimiter char." ;; Do nothing if the \verb construct is itself inside a comment or ;; verbatim env. - (save-excursion + (unless (nth 8 (save-excursion (syntax-ppss start))) ;; Let's find the end and mark it. - ;; We used to do it inside tex-font-lock-syntactic-face-function, but - ;; this leads to funny effects when jumping to the end of the buffer, - ;; because font-lock applies font-lock-syntactic-keywords to the whole - ;; preceding text but font-lock-syntactic-face-function only to the - ;; actually displayed text. - (goto-char end) - (let ((char (char-before))) - (skip-chars-forward (string ?^ char)) ;; Use `end' ? - (when (eq (char-syntax (preceding-char)) ?/) - (put-text-property (1- (point)) (point) 'syntax-table '(1))) + ;; This may span more than a single line, but we don't bother + ;; placing a syntax-multiline property since such multiline verbs aren't + ;; valid anyway. + (skip-chars-forward (string ?^ delim)) (unless (eobp) - (put-text-property (point) (1+ (point)) 'syntax-table '(7)) - ;; Cause the rest of the buffer to be re-fontified. - ;; (remove-text-properties (1+ (point)) (point-max) '(fontified)) - ))) - "\"")) + (when (eq (char-syntax (preceding-char)) ?/) + (put-text-property (1- (point)) (point) + 'syntax-table (string-to-syntax "."))) + (put-text-property (point) (1+ (point)) + 'syntax-table (string-to-syntax "\""))))) ;; Use string syntax but math face for $...$. (defun tex-font-lock-syntactic-face-function (state) (let ((char (nth 3 state))) (cond - ((not char) font-lock-comment-face) + ((not char) + (if (eq 2 (nth 7 state)) tex-verbatim-face font-lock-comment-face)) ((eq char ?$) tex-math-face) + ;; A \verb element. (t tex-verbatim-face)))) @@ -1166,7 +1218,7 @@ (font-lock-unfontify-region-function . tex-font-lock-unfontify-region))) (set (make-local-variable 'syntax-propertize-function) - (syntax-propertize-via-font-lock tex-font-lock-syntactic-keywords)) + (syntax-propertize-rules latex-syntax-propertize-rules)) ;; TABs in verbatim environments don't do what you think. (set (make-local-variable 'indent-tabs-mode) nil) ;; Other vars that should be buffer-local. @@ -2812,15 +2864,15 @@ ;; syntax-table can't deal with. We could turn it ;; into a non-comment, or use `\n%' or `%^' as the comment. ;; Instead, we include it in the ^^A comment. - (eval-when-compile (string-to-syntax "< b")) - (eval-when-compile (string-to-syntax ">")))) + (string-to-syntax "< b") + (string-to-syntax ">"))) (let ((end (line-end-position))) (if (< end (point-max)) (put-text-property end (1+ end) 'syntax-table - (eval-when-compile (string-to-syntax "> b"))))) - (eval-when-compile (string-to-syntax "< b"))))) + (string-to-syntax "> b")))) + (string-to-syntax "< b")))) (defun doctex-font-lock-syntactic-face-function (state) ;; Mark DocTeX documentation, which is parsed as a style A comment @@ -2832,11 +2884,12 @@ (tex-font-lock-syntactic-face-function state) font-lock-doc-face)) -(defvar doctex-font-lock-syntactic-keywords - (append - tex-font-lock-syntactic-keywords - ;; For DocTeX comment-in-doc. - `(("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A)))))) +(eval-when-compile + (defconst doctex-syntax-propertize-rules + (syntax-propertize-precompile-rules + latex-syntax-propertize-rules + ;; For DocTeX comment-in-doc. + ("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A)))))) (defvar doctex-font-lock-keywords (append tex-font-lock-keywords @@ -2855,7 +2908,7 @@ (t x))) (cdr font-lock-defaults)))) (set (make-local-variable 'syntax-propertize-function) - (syntax-propertize-via-font-lock doctex-font-lock-syntactic-keywords))) + (syntax-propertize-rules doctex-syntax-propertize-rules))) (run-hooks 'tex-mode-load-hook)