From: Stefan Monnier Date: Mon, 20 Oct 2014 16:36:34 +0000 (-0400) Subject: * lisp/textmodes/css-mode.el (scss-mode): New major-mode. X-Git-Tag: emacs-25.0.90~2635^2~679^2~7 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=bc0e9e47b645ecd8da86eb8ae5810ebf2fd62b97;p=emacs.git * lisp/textmodes/css-mode.el (scss-mode): New major-mode. (css-mode-syntax-table): Use d style comment, to ease the scss case. (css-ident-re): Allow things like @-moz-keyframes. (scss--hash-re): New const. (css--font-lock-keywords): New function, extracted from css-font-lock-keywords. (css-font-lock-keywords): Use it. (scss-mode-syntax-table, scss-font-lock-keywords): New vars. (scss-smie--not-interpolation-p): New function. (css-smie--forward-token, css-smie--backward-token): Use it. (css-mode): Remove left-over code. * test/indent/scss-mode.scss: New file. * test/indent/css-mode.css: Add a few uneventful examples. --- diff --git a/etc/NEWS b/etc/NEWS index 91cad1315a6..e49fd748a90 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -273,6 +273,7 @@ These emulations of old editors are believed to be no longer relevant * New Modes and Packages in Emacs 25.1 +** scss-mode (a minor variant of css-mode) * Incompatible Lisp Changes in Emacs 25.1 diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 3f75bbdc355..b27f8a0d17e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,12 @@ +2014-10-20 Stefan Monnier + + * textmodes/css-mode.el (scss-mode): New major-mode. + (css-mode-syntax-table): Use d style comment, to ease the scss case. + (css-ident-re): Allow things like @-moz-keyframes. + (scss--hash-re): New const. + (css--font-lock-keywords): New function, extracted from + css-font-lock-keywords. + 2014-10-19 Ulf Jasper * net/newst-backend.el: Require url-parse. @@ -26,8 +35,8 @@ * net/newst-reader.el (newsticker-html-renderer): Whitespace. (newsticker--print-extra-elements) (newsticker--do-print-extra-element): Documentation - (newsticker--image-read): Optionally limit image height. Use - imagemagick if possible. + (newsticker--image-read): Optionally limit image height. + Use imagemagick if possible. (newsticker--icon-read): New. * net/newst-treeview.el (newsticker--treeview-item-show): Limit height of feed logo. diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el index 1a07269c9e9..175964392e9 100644 --- a/lisp/textmodes/css-mode.el +++ b/lisp/textmodes/css-mode.el @@ -185,7 +185,7 @@ (let ((st (make-syntax-table))) ;; C-style comments. (modify-syntax-entry ?/ ". 14" st) - (modify-syntax-entry ?* ". 23" st) + (modify-syntax-entry ?* ". 23b" st) ;; Strings. (modify-syntax-entry ?\" "\"" st) (modify-syntax-entry ?\' "\"" st) @@ -210,11 +210,15 @@ "\\\\\\(?:[^\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-ident-re (concat css-nmstart-re css-nmchar-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 "+")) (defconst css-proprietary-nmstart-re ;; Vendor-specific properties. (concat "[-_]" (regexp-opt '("ms" "moz" "o" "khtml" "webkit")) "-")) (defconst css-name-re (concat css-nmchar-re "+")) +(defconst scss--hash-re "#\\(?:{[$-_[:alnum:]]+}\\|[[:alnum:]]+\\)") + (defface css-selector '((t :inherit font-lock-function-name-face)) "Face to use for selectors." :group 'css) @@ -224,24 +228,44 @@ (defface css-proprietary-property '((t :inherit (css-property italic))) "Face to use for vendor-specific properties.") -(defvar css-font-lock-keywords - `(("!\\s-*important" . font-lock-builtin-face) +(defun css--font-lock-keywords (&optional sassy) + `((,(concat "!\\s-*" + (regexp-opt (append (if sassy '("global")) + '("important")))) + (0 font-lock-builtin-face)) ;; Atrules keywords. IDs not in css-at-ids are valid (ignored). ;; In fact the regexp should probably be ;; (,(concat "\\(@" css-ident-re "\\)\\([ \t\n][^;{]*\\)[;{]") ;; (1 font-lock-builtin-face)) ;; Since "An at-rule consists of everything up to and including the next ;; semicolon (;) or the next block, whichever comes first." - (,(concat "@" css-ident-re) . font-lock-builtin-face) + (,(concat "@" css-ident-re) (0 font-lock-builtin-face)) ;; Selectors. ;; FIXME: attribute selectors don't work well because they may contain ;; strings which have already been highlighted as f-l-string-face and ;; thus prevent this highlighting from being applied (actually now that - ;; I use `append' this should work better). But really the part of the + ;; I use `keep' this should work better). But really the part of the ;; selector between [...] should simply not be highlighted. - (,(concat "^\\([ \t]*[^@:{}\n][^:{}]+\\(?::" (regexp-opt css-pseudo-ids t) - "\\(?:([^)]+)\\)?[^:{\n]*\\)*\\)\\(?:\n[ \t]*\\)*{") - (1 'css-selector append)) + (,(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 "[^:{}#]*\\)*")) + "\\(?::" (regexp-opt css-pseudo-ids t) + "\\(?:([^\)]+)\\)?" + (if (not sassy) + "[^:{}\n]*" + (concat "[^:{}\n#]*\\(?:" scss--hash-re "[^:{}\n#]*\\)*")) + "\\)*" + "\\)\\(?:\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 ;; as being part of a multiline element (and even then, this only @@ -260,6 +284,8 @@ "\\)\\s-*:") (1 (if (match-end 2) 'css-proprietary-property 'css-property))))) +(defvar css-font-lock-keywords (css--font-lock-keywords)) + (defvar css-font-lock-defaults '(css-font-lock-keywords nil t)) @@ -277,6 +303,7 @@ (defun css-smie--forward-token () (cond ((and (eq (char-before) ?\}) + (scss-smie--not-interpolation-p) ;; FIXME: If the next char is not whitespace, what should we do? (or (memq (char-after) '(?\s ?\t ?\n)) (looking-at comment-start-skip))) @@ -293,7 +320,8 @@ (forward-comment (- (point))) (cond ;; FIXME: If the next char is not whitespace, what should we do? - ((and (eq (char-before) ?\}) (> pos (point))) ";") + ((and (eq (char-before) ?\}) (scss-smie--not-interpolation-p) + (> pos (point))) ";") ((memq (char-before) '(?\; ?\, ?\:)) (forward-char -1) (string (char-after))) (t (smie-default-backward-token))))) @@ -315,7 +343,6 @@ (setq-local comment-end "*/") (setq-local comment-end-skip "[ \t]*\\*+/") (setq-local parse-sexp-ignore-comments t) - (setq-local indent-line-function 'css-indent-line) (setq-local fill-paragraph-function 'css-fill-paragraph) (setq-local add-log-current-defun-function #'css-current-defun-name) (smie-setup css-smie-grammar #'css-smie-rules @@ -406,5 +433,35 @@ (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") (match-string-no-properties 1)))))) +;;; SCSS mode + +(defvar scss-mode-syntax-table + (let ((st (make-syntax-table css-mode-syntax-table))) + (modify-syntax-entry ?/ ". 124" st) + (modify-syntax-entry ?\n ">" st) + st)) + +(defvar scss-font-lock-keywords + (append `((,(concat "$" css-ident-re) (0 font-lock-variable-name-face))) + (css--font-lock-keywords 'sassy) + `((,(concat "@mixin[ \t]+\\(" css-ident-re "\\)[ \t]*(") + (1 font-lock-function-name-face))))) + +(defun scss-smie--not-interpolation-p () + (save-excursion + (forward-char -1) + (or (zerop (skip-chars-backward "[:alnum:]")) + (not (looking-back "#{\\$" (- (point) 3)))))) + +;;;###autoload (add-to-list 'auto-mode-alist '("\\.scss\\'" . scss-mode)) +;;;###autoload +(define-derived-mode scss-mode css-mode "SCSS" + "Major mode to edit \"Sassy CSS\" files." + (setq-local comment-start "// ") + (setq-local comment-end "") + (setq-local comment-start-skip "/[*/]+[ t]*") + (setq-local comment-end-skip "[ \t]*\\(?:\n\\|\\*+/\\)") + (setq-local font-lock-defaults '(scss-font-lock-keywords nil t))) + (provide 'css-mode) ;;; css-mode.el ends here diff --git a/test/ChangeLog b/test/ChangeLog index ea11f9429f0..a5ac25a92a1 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,8 @@ +2014-10-20 Stefan Monnier + + * indent/scss-mode.scss: New file. + * indent/css-mode.css: Add a few uneventful examples. + 2014-10-15 Eli Zaretskii * BidiCharacterTest.txt: New file, from Unicode. @@ -28,8 +33,8 @@ 2014-09-26 Leo Liu - * automated/cl-lib.el (cl-digit-char-p, cl-parse-integer): New - tests. (Bug#18557) + * automated/cl-lib.el (cl-digit-char-p, cl-parse-integer): + New tests. (Bug#18557) 2014-09-24 Ulf Jasper @@ -39,8 +44,8 @@ 2014-09-09 Eli Zaretskii - * automated/fns-tests.el (fns-tests-collate-sort): Bind - w32-collate-ignore-punctuation to t when sorting according to + * automated/fns-tests.el (fns-tests-collate-sort): + Bind w32-collate-ignore-punctuation to t when sorting according to UTS#10 rules. 2014-09-07 Michael Albinus @@ -555,8 +560,8 @@ * automated/subword-tests.el (subword-tests2): More subword tests. - * automated/cl-lib.el (cl-lib-keyword-names-versus-values): New - test: correct parsing of keyword arguments. + * automated/cl-lib.el (cl-lib-keyword-names-versus-values): + New test: correct parsing of keyword arguments. 2014-03-22 Dmitry Gutov @@ -651,8 +656,8 @@ 2014-02-17 Michael Albinus - * automated/tramp-tests.el (tramp-test28-shell-command): Perform - an initial `sit-for' prior the while loop. + * automated/tramp-tests.el (tramp-test28-shell-command): + Perform an initial `sit-for' prior the while loop. 2014-02-16 Michael Albinus @@ -674,8 +679,8 @@ * automated/tramp-tests.el (tramp-test26-process-file): Improve test. (tramp-test27-start-file-process): Use "_p" as argument of lambda. - (tramp-test28-shell-command): Improve `shell-command' test. Add - `async-shell-command' tests. + (tramp-test28-shell-command): Improve `shell-command' test. + Add `async-shell-command' tests. 2014-02-04 Michael Albinus @@ -731,8 +736,8 @@ 2014-01-13 Michael Albinus - * automated/ert-tests.el (ert-test-record-backtrace): Reenable - test case with adapted test string. (Bug#13064) + * automated/ert-tests.el (ert-test-record-backtrace): + Reenable test case with adapted test string. (Bug#13064) 2013-12-28 Glenn Morris diff --git a/test/indent/css-mode.css b/test/indent/css-mode.css index 4dbab06975c..564ac16f954 100644 --- a/test/indent/css-mode.css +++ b/test/indent/css-mode.css @@ -1,7 +1,17 @@ +/* asdfasdf */ + .xxx { } +article[role="main"] { + width: 60%; +} + +/* asdfasdf */ +@foo x2 { + bla:toto; +} .x2 { foo: bar; diff --git a/test/indent/scss-mode.scss b/test/indent/scss-mode.scss new file mode 100644 index 00000000000..7a29929efca --- /dev/null +++ b/test/indent/scss-mode.scss @@ -0,0 +1,57 @@ +// Comment! + +nav { + ul { + margin: 0; /* More comment */ + padding: 0; + list-style: none; + } + + li { display: inline-block; } + + a { + display: block; + padding: 6px 12px; + text-decoration: none; + } +} +nav ul { + margin: 0; + padding: 0; + list-style: none; +} + +nav li { + display: inline-block; +} + +nav a var +{ + display: block; + padding: 6px 12px; + text-decoration: none; +} + +$name: foo; +$attr: border; +p.#{$name} var +{ + x#{$attr}-color: blue; +} +article[role="main"] { + $toto: 500 !global; + float: left; + width: 600px / 888px * 100%; + height: 100px / 888px * 100%; +} + +@import 'reset'; + +@mixin border-radius($radius) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + -ms-border-radius: $radius; + border-radius: $radius; +} + +.box { @include border-radius(10px); }