Don't freeze Emacs on colour codes in sccs-mode
* lisp/textmodes/css-mode.el (css--font-lock-keywords): Don't freeze Emacs on #ffffff #ffffff, and be more strict in parsing selectors (bug#53203).
This commit is contained in:
parent
b26574d7d7
commit
0da7689b16
4 changed files with 166 additions and 21 deletions
|
@ -928,6 +928,32 @@ cannot be completed sensibly: `custom-ident',
|
|||
(defface css-proprietary-property '((t :inherit (css-property italic)))
|
||||
"Face to use for vendor-specific properties.")
|
||||
|
||||
(defun css--selector-regexp (sassy)
|
||||
(concat
|
||||
"\\(?:"
|
||||
(if (not sassy)
|
||||
"[-_%*#.>[:alnum:]]+"
|
||||
;; Same as for non-sassy except we do want to allow { and }
|
||||
;; chars in selectors in the case of #{$foo}
|
||||
;; variable interpolation!
|
||||
(concat "\\(?:[-_%*#.>[:alnum:]]*" scss--hash-re
|
||||
"\\|[-_%*#.>[:alnum:]]+\\)"))
|
||||
;; Even though pseudo-elements should be prefixed by ::, a
|
||||
;; single colon is accepted for backward compatibility.
|
||||
"\\(?:\\(:" (regexp-opt (append css-pseudo-class-ids
|
||||
css-pseudo-element-ids)
|
||||
t)
|
||||
"\\|::" (regexp-opt css-pseudo-element-ids t) "\\)\\)?"
|
||||
;; Braces after selectors.
|
||||
"\\(?:\\[[^]\n]+\\]\\)?"
|
||||
;; Parentheses after selectors.
|
||||
"\\(?:([^)]+)\\)?"
|
||||
;; Main bit over. But perhaps just [target]?
|
||||
"\\|\\[[^]\n]+\\]"
|
||||
;; :root, ::marker and the like.
|
||||
"\\|::?[[:alnum:]]+\\(?:([^)]+)\\)?"
|
||||
"\\)"))
|
||||
|
||||
(defun css--font-lock-keywords (&optional sassy)
|
||||
`((,(concat "!\\s-*" (regexp-opt css--bang-ids))
|
||||
(0 font-lock-builtin-face))
|
||||
|
@ -948,28 +974,16 @@ cannot be completed sensibly: `custom-ident',
|
|||
;; selector between [...] should simply not be highlighted.
|
||||
(,(concat
|
||||
"^[ \t]*\\("
|
||||
(if (not sassy)
|
||||
;; We don't allow / as first char, so as not to
|
||||
;; take a comment as the beginning of a selector.
|
||||
"[^@/:{}() \t\n][^:{}()]*"
|
||||
;; Same as for non-sassy except we do want to allow { and }
|
||||
;; chars in selectors in the case of #{$foo}
|
||||
;; variable interpolation!
|
||||
(concat "\\(?:" scss--hash-re
|
||||
"\\|[^@/:{}() \t\n#]\\)"
|
||||
"[^:{}()#]*\\(?:" scss--hash-re "[^:{}()#]*\\)*"))
|
||||
;; Even though pseudo-elements should be prefixed by ::, a
|
||||
;; single colon is accepted for backward compatibility.
|
||||
"\\(?:\\(:" (regexp-opt (append css-pseudo-class-ids
|
||||
css-pseudo-element-ids)
|
||||
t)
|
||||
"\\|::" (regexp-opt css-pseudo-element-ids t) "\\)"
|
||||
"\\(?:([^)]+)\\)?"
|
||||
(if (not sassy)
|
||||
"[^:{}()\n]*"
|
||||
(concat "[^:{}()\n#]*\\(?:" scss--hash-re "[^:{}()\n#]*\\)*"))
|
||||
;; We have at least one selector.
|
||||
(css--selector-regexp sassy)
|
||||
;; And then possibly more.
|
||||
"\\(?:"
|
||||
;; Separators between selectors.
|
||||
"[ \n\t,+~>]+"
|
||||
(css--selector-regexp sassy)
|
||||
"\\)*"
|
||||
"\\)\\(?:\n[ \t]*\\)*{")
|
||||
;; And then a brace.
|
||||
"\\)[ \n\t]*{")
|
||||
(1 'css-selector keep))
|
||||
;; In the above rule, we allow the open-brace to be on some subsequent
|
||||
;; line. This will only work if we properly mark the intervening text
|
||||
|
|
56
test/lisp/textmodes/css-mode-resources/css-selectors.txt
Normal file
56
test/lisp/textmodes/css-mode-resources/css-selectors.txt
Normal file
|
@ -0,0 +1,56 @@
|
|||
#firstname
|
||||
*
|
||||
p
|
||||
p.intro
|
||||
div, p
|
||||
div p
|
||||
div > p
|
||||
div + p
|
||||
p ~ ul
|
||||
[target]
|
||||
[target=_blank]
|
||||
[title~=flower]
|
||||
[lang|=en]
|
||||
a[href^="https"]
|
||||
a[href$=".pdf"]
|
||||
a[href*="w3schools"]
|
||||
a:active
|
||||
p::after
|
||||
p::before
|
||||
input:checked
|
||||
input:default
|
||||
input:disabled
|
||||
p:empty
|
||||
input:enabled
|
||||
p:first-child
|
||||
p::first-letter
|
||||
p::first-line
|
||||
p:first-of-type
|
||||
input:focus
|
||||
:fullscreen
|
||||
a:hover
|
||||
input:in-range
|
||||
input:indeterminate
|
||||
input:invalid
|
||||
p:lang(it)
|
||||
p:last-child
|
||||
p:last-of-type
|
||||
a:link
|
||||
::marker
|
||||
:not(p)
|
||||
p:nth-child(2)
|
||||
p:nth-last-child(2)
|
||||
p:nth-last-of-type(2)
|
||||
p:nth-of-type(2)
|
||||
p:only-of-type
|
||||
p:only-child
|
||||
input:optional
|
||||
input:out-of-range
|
||||
input:read-only
|
||||
input:read-write
|
||||
input:required
|
||||
:root
|
||||
::selection
|
||||
#news:target
|
||||
input:valid
|
||||
a:visited
|
|
@ -0,0 +1,6 @@
|
|||
p.#{$name} var
|
||||
p.#{$name}:active var
|
||||
p.#{$name}::after var
|
||||
f.#{$bar}::after p::after
|
||||
p.#{$name} f.#{$bar} k.var #{$bar} #{$bar}
|
||||
p.#{$name}
|
|
@ -419,5 +419,74 @@
|
|||
(indent-region (point-min) (point-max))
|
||||
(should (equal (buffer-string) orig)))))
|
||||
|
||||
(ert-deftest css-mode-test-selectors ()
|
||||
(let ((selectors
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (ert-resource-file "css-selectors.txt"))
|
||||
(string-lines (buffer-string)))))
|
||||
(with-suppressed-warnings ((interactive font-lock-debug-fontif))
|
||||
(dolist (selector selectors)
|
||||
(with-temp-buffer
|
||||
(css-mode)
|
||||
(insert selector " {\n}\n")
|
||||
(font-lock-debug-fontify)
|
||||
(goto-char (point-min))
|
||||
(unless (eq (get-text-property (point) 'face)
|
||||
'css-selector)
|
||||
(should-not (format "Didn't recognize %s as a selector"
|
||||
(buffer-substring-no-properties
|
||||
(point) (line-end-position)))))))
|
||||
;; Test many selectors.
|
||||
(dolist (selector selectors)
|
||||
(with-temp-buffer
|
||||
(css-mode)
|
||||
(insert selector " ")
|
||||
(dotimes (_ (random 5))
|
||||
(insert (seq-random-elt '(" , " " > " " + "))
|
||||
(seq-random-elt selectors)))
|
||||
(insert "{\n}\n")
|
||||
(font-lock-debug-fontify)
|
||||
(goto-char (point-min))
|
||||
(unless (eq (get-text-property (point) 'face)
|
||||
'css-selector)
|
||||
(should-not (format "Didn't recognize %s as a selector"
|
||||
(buffer-substring-no-properties
|
||||
(point) (line-end-position)))))))
|
||||
;; Test wrong separators.
|
||||
(dolist (selector selectors)
|
||||
(with-temp-buffer
|
||||
(css-mode)
|
||||
(insert selector " ")
|
||||
(dotimes (_ (1+ (random 5)))
|
||||
(insert (seq-random-elt '("=" " @ "))
|
||||
(seq-random-elt selectors)))
|
||||
(insert "{\n}\n")
|
||||
(font-lock-debug-fontify)
|
||||
(goto-char (point-min))
|
||||
(when (eq (get-text-property (point) 'face)
|
||||
'css-selector)
|
||||
(should-not (format "Recognized %s as a selector"
|
||||
(buffer-substring-no-properties
|
||||
(point) (line-end-position))))))))))
|
||||
|
||||
(ert-deftest scss-mode-test-selectors ()
|
||||
(let ((selectors
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (ert-resource-file "scss-selectors.txt"))
|
||||
(string-lines (buffer-string)))))
|
||||
(with-suppressed-warnings ((interactive font-lock-debug-fontif))
|
||||
(dolist (selector selectors)
|
||||
(with-temp-buffer
|
||||
(scss-mode)
|
||||
(insert selector " {\n}\n")
|
||||
(font-lock-debug-fontify)
|
||||
(goto-char (point-min))
|
||||
(unless (eq (get-text-property (point) 'face)
|
||||
'css-selector)
|
||||
(should-not (format "Didn't recognize %s as a selector"
|
||||
(buffer-substring-no-properties
|
||||
(point) (line-end-position))))))))))
|
||||
|
||||
|
||||
(provide 'css-mode-tests)
|
||||
;;; css-mode-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue