Commit b879a6e2 authored by Stefan Monnier's avatar Stefan Monnier
Browse files

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.
parent 269c197e
......@@ -236,6 +236,8 @@ kill ring).
* 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.
......
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):
......
......@@ -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 @@ Put first the functions more likely to cause a change and cheaper to compute.")
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 @@ Also SYNTAX is free to move point, in which case RULES may not be applied to
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 @@ Note: back-references in REGEXPs do not work."
(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 @@ Note: back-references in REGEXPs do not work."
code))))
(push (cons condition (nreverse code))
branches))
(incf offset (regexp-opt-depth re))
(incf offset (regexp-opt-depth orig-re))
re))
rules
"\\|")))
......
......@@ -483,19 +483,27 @@ 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
;; 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 @@ with no args, if that value is non-nil."
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 @@ with no args, if that value is non-nil."
"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'."
......
......@@ -3304,7 +3304,7 @@ Key bindings:
(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)
......
......@@ -488,10 +488,6 @@ An alternative value is \" . \", if you use a font with a narrow period."
;; (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 @@ An alternative value is \" . \", if you use a font with a narrow period."
(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 @@ Not smaller than the value set by `tex-suscript-height-minimum'."
(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 @@ Entering SliTeX mode runs the hook `text-mode-hook', then the hook
(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 @@ There might be text before point."
;; 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 @@ There might be text before point."
(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 @@ There might be text before point."
(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)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment