;;; css-mode.el --- Major mode to edit CSS files -*- lexical-binding: t -*-
-;; Copyright (C) 2006-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Maintainer: Simen Heggestøyl <simenheg@gmail.com>
(defconst css-pseudo-class-ids
'("active" "checked" "disabled" "empty" "enabled" "first"
"first-child" "first-of-type" "focus" "hover" "indeterminate" "lang"
- "last-child" "last-of-type" "left" "link" "nth-child"
+ "last-child" "last-of-type" "left" "link" "not" "nth-child"
"nth-last-child" "nth-last-of-type" "nth-of-type" "only-child"
"only-of-type" "right" "root" "target" "visited")
"Identifiers for pseudo-classes.")
"list-style" "list-style-image" "list-style-position"
"list-style-type" "margin" "margin-bottom" "margin-left"
"margin-right" "margin-top" "max-height" "max-width" "min-height"
- "min-width" "orphans" "overflow" "padding" "padding-bottom"
- "padding-left" "padding-right" "padding-top" "page-break-after"
+ "min-width" "orphans" "padding" "padding-bottom" "padding-left"
+ "padding-right" "padding-top" "page-break-after"
"page-break-before" "page-break-inside" "pause" "pause-after"
"pause-before" "pitch" "pitch-range" "play-during" "position"
"quotes" "richness" "right" "speak" "speak-header" "speak-numeral"
"font-variant-east-asian" "font-variant-ligatures"
"font-variant-numeric" "font-variant-position" "font-weight"
+ ;; CSS Overflow Module Level 3
+ ;; (http://www.w3.org/TR/css-overflow-3/#property-index)
+ "max-lines" "overflow" "overflow-x" "overflow-y"
+
;; CSS Text Decoration Module Level 3
;; (http://dev.w3.org/csswg/css-text-decor-3/#property-index)
"text-decoration" "text-decoration-color" "text-decoration-line"
(modify-syntax-entry ?- "_" st)
st))
+(eval-and-compile
+ (defconst css--uri-re
+ (concat
+ "url\\((\\)[[:space:]]*\\(?:\\\\.\\|[^()[:space:]\n'\"]\\)+"
+ "[[:space:]]*\\()\\)")))
+
+(defconst css-syntax-propertize-function
+ (syntax-propertize-rules
+ (css--uri-re (1 "|") (2 "|"))))
+
(defconst css-escapes-re
"\\\\\\(?:[^\000-\037\177]\\|[0-9a-fA-F]+[ \n\t\r\f]?\\)")
(defconst css-nmchar-re (concat "\\(?:[-[:alnum:]]\\|" css-escapes-re "\\)"))
-(defconst css-nmstart-re (concat "\\(?:[[:alpha:]]\\|" css-escapes-re "\\)"))
+(defconst css-nmstart-re (concat "\\(?:--\\)?\\(?:[[:alpha:]]\\|" css-escapes-re "\\)"))
(defconst css-ident-re ;; (concat css-nmstart-re css-nmchar-re "*")
;; Apparently, "at rules" names can start with a dash, e.g. @-moz-keyframes.
(concat css-nmchar-re "+"))
(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][^:{}]+"
+ "[^@/:{}() \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 "[^:{}#]*\\)*"))
+ "\\|[^@/:{}() \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#]*\\)*"))
+ "[^:{}()\n]*"
+ (concat "[^:{}()\n#]*\\(?:" scss--hash-re "[^:{}()\n#]*\\)*"))
"\\)*"
"\\)\\(?:\n[ \t]*\\)*{")
(1 'css-selector keep))
"\\(?:\\(" css-proprietary-nmstart-re "\\)\\|"
css-nmstart-re "\\)" css-nmchar-re "*"
"\\)\\s-*:")
- (1 (if (match-end 2) 'css-proprietary-property 'css-property)))))
+ (1 (if (match-end 2) 'css-proprietary-property 'css-property)))
+ ;; Make sure the parens in a url(...) expression receive the
+ ;; default face. This is done because the parens may sometimes
+ ;; receive generic string delimiter syntax (see
+ ;; `css-syntax-propertize-function').
+ (,css--uri-re
+ (1 'default t) (2 'default t))))
(defvar css-font-lock-keywords (css--font-lock-keywords))
(defcustom css-indent-offset 4
"Basic size of one indentation step."
:version "22.2"
- :type 'integer)
+ :type 'integer
+ :safe 'integerp)
(require 'smie)
(`(:elem . basic) css-indent-offset)
(`(:elem . arg) 0)
(`(:list-intro . ,(or `";" `"")) t) ;"" stands for BOB (bug#15467).
+ (`(:before . "{")
+ (when (or (smie-rule-hanging-p) (smie-rule-bolp))
+ (smie-backward-sexp ";")
+ (smie-indent-virtual)))
(`(:before . ,(or "{" "("))
(if (smie-rule-hanging-p) (smie-rule-parent 0)))))
(css--complete-at-rule)))
;;;###autoload
-(define-derived-mode css-mode fundamental-mode "CSS"
+(define-derived-mode css-mode prog-mode "CSS"
"Major mode to edit Cascading Style Sheets."
(setq-local font-lock-defaults css-font-lock-defaults)
(setq-local comment-start "/*")
(setq-local comment-start-skip "/\\*+[ \t]*")
(setq-local comment-end "*/")
(setq-local comment-end-skip "[ \t]*\\*+/")
- (setq-local parse-sexp-ignore-comments t)
- (setq-local fill-paragraph-function 'css-fill-paragraph)
+ (setq-local syntax-propertize-function
+ css-syntax-propertize-function)
+ (setq-local fill-paragraph-function #'css-fill-paragraph)
+ (setq-local adaptive-fill-function #'css-adaptive-fill)
(setq-local add-log-current-defun-function #'css-current-defun-name)
(smie-setup css-smie-grammar #'css-smie-rules
:forward-token #'css-smie--forward-token
(defun css-fill-paragraph (&optional justify)
(save-excursion
+ ;; Fill succeeding comment when invoked right before a multi-line
+ ;; comment.
+ (when (save-excursion
+ (beginning-of-line)
+ (comment-search-forward (point-at-eol) t))
+ (goto-char (match-end 0)))
(let ((ppss (syntax-ppss))
(eol (line-end-position)))
(cond
(paragraph-separate
(if (and comment-continue
(string-match "[^ \t]" comment-continue))
- (concat "\\(?:[ \t]*" (regexp-quote comment-continue)
- "\\)?\\(?:" paragraph-separate "\\)")
+ (concat "\\(?:[ \t]*\\(?:"
+ (regexp-quote comment-continue) "\\|"
+ comment-start-skip "\\|"
+ comment-end-skip "\\)\\)?"
+ "\\(?:" paragraph-separate "\\)")
paragraph-separate))
(paragraph-start
(if (and comment-continue
;; Don't use the default filling code.
t)))))))
+(defun css-adaptive-fill ()
+ (when (looking-at "[ \t]*/\\*[ \t]*")
+ (let ((str (match-string 0)))
+ (and (string-match "/\\*" str)
+ (replace-match " *" t t str)))))
+
(defun css-current-defun-name ()
"Return the name of the CSS section at point, or nil."
(save-excursion
"Major mode to edit \"Sassy CSS\" files."
(setq-local comment-start "// ")
(setq-local comment-end "")
+ (setq-local comment-continue " *")
(setq-local comment-start-skip "/[*/]+[ \t]*")
(setq-local comment-end-skip "[ \t]*\\(?:\n\\|\\*+/\\)")
(setq-local font-lock-defaults '(scss-font-lock-keywords nil t)))