]> code.delx.au - gnu-emacs/blob - lisp/textmodes/css-mode.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / textmodes / css-mode.el
1 ;;; css-mode.el --- Major mode to edit CSS files -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
4
5 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6 ;; Maintainer: Simen Heggestøyl <simenheg@gmail.com>
7 ;; Keywords: hypermedia
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; Yet another CSS mode.
27
28 ;;; Todo:
29
30 ;; - electric ; and }
31 ;; - filling code with auto-fill-mode
32 ;; - attribute value completion
33 ;; - fix font-lock errors with multi-line selectors
34
35 ;;; Code:
36
37 (defgroup css nil
38 "Cascading Style Sheets (CSS) editing mode."
39 :group 'languages)
40
41 (defconst css-pseudo-class-ids
42 '("active" "checked" "disabled" "empty" "enabled" "first"
43 "first-child" "first-of-type" "focus" "hover" "indeterminate" "lang"
44 "last-child" "last-of-type" "left" "link" "not" "nth-child"
45 "nth-last-child" "nth-last-of-type" "nth-of-type" "only-child"
46 "only-of-type" "right" "root" "target" "visited")
47 "Identifiers for pseudo-classes.")
48
49 (defconst css-pseudo-element-ids
50 '("after" "before" "first-letter" "first-line")
51 "Identifiers for pseudo-elements.")
52
53 (defconst css-at-ids
54 '("charset" "font-face" "import" "media" "namespace" "page")
55 "Identifiers that appear in the form @foo.")
56
57 (defconst css-descriptor-ids
58 '("ascent" "baseline" "bbox" "cap-height" "centerline" "definition-src"
59 "descent" "font-family" "font-size" "font-stretch" "font-style"
60 "font-variant" "font-weight" "mathline" "panose-1" "slope" "src" "stemh"
61 "stemv" "topline" "unicode-range" "units-per-em" "widths" "x-height")
62 "Identifiers for font descriptors.")
63
64 (defconst css-media-ids
65 '("all" "aural" "bitmap" "continuous" "grid" "paged" "static" "tactile"
66 "visual")
67 "Identifiers for types of media.")
68
69 (defconst css-property-ids
70 '(;; CSS 2.1 properties (http://www.w3.org/TR/CSS21/propidx.html).
71 ;;
72 ;; Properties duplicated by any of the CSS3 modules below have
73 ;; been removed.
74 "azimuth" "border-collapse" "border-spacing" "bottom"
75 "caption-side" "clear" "clip" "content" "counter-increment"
76 "counter-reset" "cue" "cue-after" "cue-before" "direction" "display"
77 "elevation" "empty-cells" "float" "height" "left" "line-height"
78 "list-style" "list-style-image" "list-style-position"
79 "list-style-type" "margin" "margin-bottom" "margin-left"
80 "margin-right" "margin-top" "max-height" "max-width" "min-height"
81 "min-width" "orphans" "padding" "padding-bottom" "padding-left"
82 "padding-right" "padding-top" "page-break-after"
83 "page-break-before" "page-break-inside" "pause" "pause-after"
84 "pause-before" "pitch" "pitch-range" "play-during" "position"
85 "quotes" "richness" "right" "speak" "speak-header" "speak-numeral"
86 "speak-punctuation" "speech-rate" "stress" "table-layout" "top"
87 "unicode-bidi" "vertical-align" "visibility" "voice-family" "volume"
88 "widows" "width" "z-index"
89
90 ;; CSS Animations
91 ;; (http://www.w3.org/TR/css3-animations/#property-index)
92 "animation" "animation-delay" "animation-direction"
93 "animation-duration" "animation-fill-mode"
94 "animation-iteration-count" "animation-name"
95 "animation-play-state" "animation-timing-function"
96
97 ;; CSS Backgrounds and Borders Module Level 3
98 ;; (http://www.w3.org/TR/css3-background/#property-index)
99 "background" "background-attachment" "background-clip"
100 "background-color" "background-image" "background-origin"
101 "background-position" "background-repeat" "background-size"
102 "border" "border-bottom" "border-bottom-color"
103 "border-bottom-left-radius" "border-bottom-right-radius"
104 "border-bottom-style" "border-bottom-width" "border-color"
105 "border-image" "border-image-outset" "border-image-repeat"
106 "border-image-slice" "border-image-source" "border-image-width"
107 "border-left" "border-left-color" "border-left-style"
108 "border-left-width" "border-radius" "border-right"
109 "border-right-color" "border-right-style" "border-right-width"
110 "border-style" "border-top" "border-top-color"
111 "border-top-left-radius" "border-top-right-radius"
112 "border-top-style" "border-top-width" "border-width" "box-shadow"
113
114 ;; CSS Basic User Interface Module Level 3 (CSS3 UI)
115 ;; (http://www.w3.org/TR/css3-ui/#property-index)
116 "box-sizing" "caret-color" "cursor" "nav-down" "nav-left"
117 "nav-right" "nav-up" "outline" "outline-color" "outline-offset"
118 "outline-style" "outline-width" "resize" "text-overflow"
119
120 ;; CSS Color Module Level 3
121 ;; (http://www.w3.org/TR/css3-color/#property)
122 "color" "opacity"
123
124 ;; CSS Flexible Box Layout Module Level 1
125 ;; (http://www.w3.org/TR/css-flexbox-1/#property-index)
126 "align-content" "align-items" "align-self" "flex" "flex-basis"
127 "flex-direction" "flex-flow" "flex-grow" "flex-shrink" "flex-wrap"
128 "justify-content" "order"
129
130 ;; CSS Fonts Module Level 3
131 ;; (http://www.w3.org/TR/css3-fonts/#property-index)
132 "font" "font-family" "font-feature-settings" "font-kerning"
133 "font-language-override" "font-size" "font-size-adjust"
134 "font-stretch" "font-style" "font-synthesis" "font-variant"
135 "font-variant-alternates" "font-variant-caps"
136 "font-variant-east-asian" "font-variant-ligatures"
137 "font-variant-numeric" "font-variant-position" "font-weight"
138
139 ;; CSS Overflow Module Level 3
140 ;; (http://www.w3.org/TR/css-overflow-3/#property-index)
141 "max-lines" "overflow" "overflow-x" "overflow-y"
142
143 ;; CSS Text Decoration Module Level 3
144 ;; (http://dev.w3.org/csswg/css-text-decor-3/#property-index)
145 "text-decoration" "text-decoration-color" "text-decoration-line"
146 "text-decoration-skip" "text-decoration-style" "text-emphasis"
147 "text-emphasis-color" "text-emphasis-position" "text-emphasis-style"
148 "text-shadow" "text-underline-position"
149
150 ;; CSS Text Module Level 3
151 ;; (http://www.w3.org/TR/css3-text/#property-index)
152 "hanging-punctuation" "hyphens" "letter-spacing" "line-break"
153 "overflow-wrap" "tab-size" "text-align" "text-align-last"
154 "text-indent" "text-justify" "text-transform" "white-space"
155 "word-break" "word-spacing" "word-wrap"
156
157 ;; CSS Transforms Module Level 1
158 ;; (http://www.w3.org/TR/css3-2d-transforms/#property-index)
159 "backface-visibility" "perspective" "perspective-origin"
160 "transform" "transform-origin" "transform-style"
161
162 ;; CSS Transitions
163 ;; (http://www.w3.org/TR/css3-transitions/#property-index)
164 "transition" "transition-delay" "transition-duration"
165 "transition-property" "transition-timing-function"
166
167 ;; Filter Effects Module Level 1
168 ;; (http://www.w3.org/TR/filter-effects/#property-index)
169 "color-interpolation-filters" "filter" "flood-color"
170 "flood-opacity" "lighting-color")
171 "Identifiers for properties.")
172
173 (defcustom css-electric-keys '(?\} ?\;) ;; '()
174 "Self inserting keys which should trigger re-indentation."
175 :version "22.2"
176 :type '(repeat character)
177 :options '((?\} ?\;))
178 :group 'css)
179
180 (defvar css-mode-syntax-table
181 (let ((st (make-syntax-table)))
182 ;; C-style comments.
183 (modify-syntax-entry ?/ ". 14" st)
184 (modify-syntax-entry ?* ". 23b" st)
185 ;; Strings.
186 (modify-syntax-entry ?\" "\"" st)
187 (modify-syntax-entry ?\' "\"" st)
188 ;; Blocks.
189 (modify-syntax-entry ?\{ "(}" st)
190 (modify-syntax-entry ?\} "){" st)
191 ;; Args in url(...) thingies and other "function calls".
192 (modify-syntax-entry ?\( "()" st)
193 (modify-syntax-entry ?\) ")(" st)
194 ;; To match attributes in selectors.
195 (modify-syntax-entry ?\[ "(]" st)
196 (modify-syntax-entry ?\] ")[" st)
197 ;; Special chars that sometimes come at the beginning of words.
198 (modify-syntax-entry ?@ "'" st)
199 ;; (modify-syntax-entry ?: "'" st)
200 (modify-syntax-entry ?# "'" st)
201 ;; Distinction between words and symbols.
202 (modify-syntax-entry ?- "_" st)
203 st))
204
205 (eval-and-compile
206 (defconst css--uri-re
207 (concat
208 "url\\((\\)[[:space:]]*\\(?:\\\\.\\|[^()[:space:]\n'\"]\\)+"
209 "[[:space:]]*\\()\\)")))
210
211 (defconst css-syntax-propertize-function
212 (syntax-propertize-rules
213 (css--uri-re (1 "|") (2 "|"))))
214
215 (defconst css-escapes-re
216 "\\\\\\(?:[^\000-\037\177]\\|[0-9a-fA-F]+[ \n\t\r\f]?\\)")
217 (defconst css-nmchar-re (concat "\\(?:[-[:alnum:]]\\|" css-escapes-re "\\)"))
218 (defconst css-nmstart-re (concat "\\(?:[[:alpha:]]\\|" css-escapes-re "\\)"))
219 (defconst css-ident-re ;; (concat css-nmstart-re css-nmchar-re "*")
220 ;; Apparently, "at rules" names can start with a dash, e.g. @-moz-keyframes.
221 (concat css-nmchar-re "+"))
222 (defconst css-proprietary-nmstart-re ;; Vendor-specific properties.
223 (concat "[-_]" (regexp-opt '("ms" "moz" "o" "khtml" "webkit")) "-"))
224 (defconst css-name-re (concat css-nmchar-re "+"))
225
226 (defconst scss--hash-re "#\\(?:{[$-_[:alnum:]]+}\\|[[:alnum:]]+\\)")
227
228 (defface css-selector '((t :inherit font-lock-function-name-face))
229 "Face to use for selectors."
230 :group 'css)
231 (defface css-property '((t :inherit font-lock-variable-name-face))
232 "Face to use for properties."
233 :group 'css)
234 (defface css-proprietary-property '((t :inherit (css-property italic)))
235 "Face to use for vendor-specific properties.")
236
237 (defun css--font-lock-keywords (&optional sassy)
238 `((,(concat "!\\s-*"
239 (regexp-opt (append (if sassy '("global"))
240 '("important"))))
241 (0 font-lock-builtin-face))
242 ;; Atrules keywords. IDs not in css-at-ids are valid (ignored).
243 ;; In fact the regexp should probably be
244 ;; (,(concat "\\(@" css-ident-re "\\)\\([ \t\n][^;{]*\\)[;{]")
245 ;; (1 font-lock-builtin-face))
246 ;; Since "An at-rule consists of everything up to and including the next
247 ;; semicolon (;) or the next block, whichever comes first."
248 (,(concat "@" css-ident-re) (0 font-lock-builtin-face))
249 ;; Variables.
250 (,(concat "--" css-ident-re) (0 font-lock-variable-name-face))
251 ;; Selectors.
252 ;; FIXME: attribute selectors don't work well because they may contain
253 ;; strings which have already been highlighted as f-l-string-face and
254 ;; thus prevent this highlighting from being applied (actually now that
255 ;; I use `keep' this should work better). But really the part of the
256 ;; selector between [...] should simply not be highlighted.
257 (,(concat
258 "^[ \t]*\\("
259 (if (not sassy)
260 ;; We don't allow / as first char, so as not to
261 ;; take a comment as the beginning of a selector.
262 "[^@/:{}() \t\n][^:{}()]+"
263 ;; Same as for non-sassy except we do want to allow { and }
264 ;; chars in selectors in the case of #{$foo}
265 ;; variable interpolation!
266 (concat "\\(?:" scss--hash-re
267 "\\|[^@/:{}() \t\n#]\\)"
268 "[^:{}()#]*\\(?:" scss--hash-re "[^:{}()#]*\\)*"))
269 ;; Even though pseudo-elements should be prefixed by ::, a
270 ;; single colon is accepted for backward compatibility.
271 "\\(?:\\(:" (regexp-opt (append css-pseudo-class-ids
272 css-pseudo-element-ids) t)
273 "\\|\\::" (regexp-opt css-pseudo-element-ids t) "\\)"
274 "\\(?:([^)]+)\\)?"
275 (if (not sassy)
276 "[^:{}()\n]*"
277 (concat "[^:{}()\n#]*\\(?:" scss--hash-re "[^:{}()\n#]*\\)*"))
278 "\\)*"
279 "\\)\\(?:\n[ \t]*\\)*{")
280 (1 'css-selector keep))
281 ;; In the above rule, we allow the open-brace to be on some subsequent
282 ;; line. This will only work if we properly mark the intervening text
283 ;; as being part of a multiline element (and even then, this only
284 ;; ensures proper refontification, but not proper discovery).
285 ("^[ \t]*{" (0 (save-excursion
286 (goto-char (match-beginning 0))
287 (skip-chars-backward " \n\t")
288 (put-text-property (point) (match-end 0)
289 'font-lock-multiline t)
290 ;; No face.
291 nil)))
292 ;; Properties. Again, we don't limit ourselves to css-property-ids.
293 (,(concat "\\(?:[{;]\\|^\\)[ \t]*\\("
294 "\\(?:\\(" css-proprietary-nmstart-re "\\)\\|"
295 css-nmstart-re "\\)" css-nmchar-re "*"
296 "\\)\\s-*:")
297 (1 (if (match-end 2) 'css-proprietary-property 'css-property)))
298 ;; Make sure the parens in a url(...) expression receive the
299 ;; default face. This is done because the parens may sometimes
300 ;; receive generic string delimiter syntax (see
301 ;; `css-syntax-propertize-function').
302 (,css--uri-re
303 (1 'default t) (2 'default t))))
304
305 (defvar css-font-lock-keywords (css--font-lock-keywords))
306
307 (defvar css-font-lock-defaults
308 '(css-font-lock-keywords nil t))
309
310 (defcustom css-indent-offset 4
311 "Basic size of one indentation step."
312 :version "22.2"
313 :type 'integer
314 :safe 'integerp)
315
316 (require 'smie)
317
318 (defconst css-smie-grammar
319 (smie-prec2->grammar
320 (smie-precs->prec2 '((assoc ";") (assoc ",") (left ":")))))
321
322 (defun css-smie--forward-token ()
323 (cond
324 ((and (eq (char-before) ?\})
325 (scss-smie--not-interpolation-p)
326 ;; FIXME: If the next char is not whitespace, what should we do?
327 (or (memq (char-after) '(?\s ?\t ?\n))
328 (looking-at comment-start-skip)))
329 (if (memq (char-after) '(?\s ?\t ?\n))
330 (forward-char 1) (forward-comment 1))
331 ";")
332 ((progn (forward-comment (point-max))
333 (looking-at "[;,:]"))
334 (forward-char 1) (match-string 0))
335 (t (smie-default-forward-token))))
336
337 (defun css-smie--backward-token ()
338 (let ((pos (point)))
339 (forward-comment (- (point)))
340 (cond
341 ;; FIXME: If the next char is not whitespace, what should we do?
342 ((and (eq (char-before) ?\}) (scss-smie--not-interpolation-p)
343 (> pos (point))) ";")
344 ((memq (char-before) '(?\; ?\, ?\:))
345 (forward-char -1) (string (char-after)))
346 (t (smie-default-backward-token)))))
347
348 (defun css-smie-rules (kind token)
349 (pcase (cons kind token)
350 (`(:elem . basic) css-indent-offset)
351 (`(:elem . arg) 0)
352 (`(:list-intro . ,(or `";" `"")) t) ;"" stands for BOB (bug#15467).
353 (`(:before . "{")
354 (when (or (smie-rule-hanging-p) (smie-rule-bolp))
355 (smie-backward-sexp ";")
356 (smie-indent-virtual)))
357 (`(:before . ,(or "{" "("))
358 (if (smie-rule-hanging-p) (smie-rule-parent 0)))))
359
360 ;;; Completion
361
362 (defun css--complete-property ()
363 "Complete property at point."
364 (save-excursion
365 (let ((pos (point)))
366 (skip-chars-backward "-[:alnum:]")
367 (let ((start (point)))
368 (skip-chars-backward " \t\r\n")
369 (when (memq (char-before) '(?\{ ?\;))
370 (list start pos css-property-ids))))))
371
372 (defun css--complete-pseudo-element-or-class ()
373 "Complete pseudo-element or pseudo-class at point."
374 (save-excursion
375 (let ((pos (point)))
376 (skip-chars-backward "-[:alnum:]")
377 (when (eq (char-before) ?\:)
378 (list (point) pos
379 (if (eq (char-before (- (point) 1)) ?\:)
380 css-pseudo-element-ids
381 css-pseudo-class-ids))))))
382
383 (defun css--complete-at-rule ()
384 "Complete at-rule (statement beginning with `@') at point."
385 (save-excursion
386 (let ((pos (point)))
387 (skip-chars-backward "-[:alnum:]")
388 (when (eq (char-before) ?\@)
389 (list (point) pos css-at-ids)))))
390
391 (defun css-completion-at-point ()
392 "Complete current symbol at point.
393 Currently supports completion of CSS properties, pseudo-elements,
394 pseudo-classes, and at-rules."
395 (or (css--complete-property)
396 (css--complete-pseudo-element-or-class)
397 (css--complete-at-rule)))
398
399 ;;;###autoload
400 (define-derived-mode css-mode prog-mode "CSS"
401 "Major mode to edit Cascading Style Sheets."
402 (setq-local font-lock-defaults css-font-lock-defaults)
403 (setq-local comment-start "/*")
404 (setq-local comment-start-skip "/\\*+[ \t]*")
405 (setq-local comment-end "*/")
406 (setq-local comment-end-skip "[ \t]*\\*+/")
407 (setq-local syntax-propertize-function
408 css-syntax-propertize-function)
409 (setq-local fill-paragraph-function #'css-fill-paragraph)
410 (setq-local adaptive-fill-function #'css-adaptive-fill)
411 (setq-local add-log-current-defun-function #'css-current-defun-name)
412 (smie-setup css-smie-grammar #'css-smie-rules
413 :forward-token #'css-smie--forward-token
414 :backward-token #'css-smie--backward-token)
415 (setq-local electric-indent-chars
416 (append css-electric-keys electric-indent-chars))
417 (add-hook 'completion-at-point-functions
418 #'css-completion-at-point nil 'local))
419
420 (defvar comment-continue)
421
422 (defun css-fill-paragraph (&optional justify)
423 (save-excursion
424 ;; Fill succeeding comment when invoked right before a multi-line
425 ;; comment.
426 (when (save-excursion
427 (beginning-of-line)
428 (comment-search-forward (point-at-eol) t))
429 (goto-char (match-end 0)))
430 (let ((ppss (syntax-ppss))
431 (eol (line-end-position)))
432 (cond
433 ((and (nth 4 ppss)
434 (save-excursion
435 (goto-char (nth 8 ppss))
436 (forward-comment 1)
437 (prog1 (not (bolp))
438 (setq eol (point)))))
439 ;; Filling inside a comment whose comment-end marker is not \n.
440 ;; This code is meant to be generic, so that it works not only for
441 ;; css-mode but for all modes.
442 (save-restriction
443 (narrow-to-region (nth 8 ppss) eol)
444 (comment-normalize-vars) ;Will define comment-continue.
445 (let ((fill-paragraph-function nil)
446 (paragraph-separate
447 (if (and comment-continue
448 (string-match "[^ \t]" comment-continue))
449 (concat "\\(?:[ \t]*\\(?:"
450 (regexp-quote comment-continue) "\\|"
451 comment-start-skip "\\|"
452 comment-end-skip "\\)\\)?"
453 "\\(?:" paragraph-separate "\\)")
454 paragraph-separate))
455 (paragraph-start
456 (if (and comment-continue
457 (string-match "[^ \t]" comment-continue))
458 (concat "\\(?:[ \t]*" (regexp-quote comment-continue)
459 "\\)?\\(?:" paragraph-start "\\)")
460 paragraph-start)))
461 (fill-paragraph justify)
462 ;; Don't try filling again.
463 t)))
464
465 ((and (null (nth 8 ppss))
466 (or (nth 1 ppss)
467 (and (ignore-errors
468 (down-list 1)
469 (when (<= (point) eol)
470 (setq ppss (syntax-ppss)))))))
471 (goto-char (nth 1 ppss))
472 (let ((end (save-excursion
473 (ignore-errors (forward-sexp 1) (copy-marker (point) t)))))
474 (when end
475 (while (re-search-forward "[{;}]" end t)
476 (cond
477 ;; This is a false positive inside a string or comment.
478 ((nth 8 (syntax-ppss)) nil)
479 ;; This is a false positive when encountering an
480 ;; interpolated variable (bug#19751).
481 ((eq (char-before (- (point) 1)) ?#) nil)
482 ((eq (char-before) ?\})
483 (save-excursion
484 (forward-char -1)
485 (skip-chars-backward " \t")
486 (when (and (not (bolp))
487 (scss-smie--not-interpolation-p))
488 (newline))))
489 (t
490 (while
491 (progn
492 (setq eol (line-end-position))
493 (and (forward-comment 1)
494 (> (point) eol)
495 ;; A multi-line comment should be on its own line.
496 (save-excursion (forward-comment -1)
497 (when (< (point) eol)
498 (newline)
499 t)))))
500 (if (< (point) eol) (newline)))))
501 (goto-char (nth 1 ppss))
502 (indent-region (line-beginning-position 2) end)
503 ;; Don't use the default filling code.
504 t)))))))
505
506 (defun css-adaptive-fill ()
507 (when (looking-at "[ \t]*/\\*[ \t]*")
508 (let ((str (match-string 0)))
509 (and (string-match "/\\*" str)
510 (replace-match " *" t t str)))))
511
512 (defun css-current-defun-name ()
513 "Return the name of the CSS section at point, or nil."
514 (save-excursion
515 (let ((max (max (point-min) (- (point) 1600)))) ; approx 20 lines back
516 (when (search-backward "{" max t)
517 (skip-chars-backward " \t\r\n")
518 (beginning-of-line)
519 (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)")
520 (match-string-no-properties 1))))))
521
522 ;;; SCSS mode
523
524 (defvar scss-mode-syntax-table
525 (let ((st (make-syntax-table css-mode-syntax-table)))
526 (modify-syntax-entry ?/ ". 124" st)
527 (modify-syntax-entry ?\n ">" st)
528 st))
529
530 (defvar scss-font-lock-keywords
531 (append `((,(concat "$" css-ident-re) (0 font-lock-variable-name-face)))
532 (css--font-lock-keywords 'sassy)
533 `((,(concat "@mixin[ \t]+\\(" css-ident-re "\\)[ \t]*(")
534 (1 font-lock-function-name-face)))))
535
536 (defun scss-smie--not-interpolation-p ()
537 (save-excursion
538 (forward-char -1)
539 (or (zerop (skip-chars-backward "-[:alnum:]"))
540 (not (looking-back "#{\\$" (- (point) 3))))))
541
542 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.scss\\'" . scss-mode))
543 ;;;###autoload
544 (define-derived-mode scss-mode css-mode "SCSS"
545 "Major mode to edit \"Sassy CSS\" files."
546 (setq-local comment-start "// ")
547 (setq-local comment-end "")
548 (setq-local comment-continue " *")
549 (setq-local comment-start-skip "/[*/]+[ \t]*")
550 (setq-local comment-end-skip "[ \t]*\\(?:\n\\|\\*+/\\)")
551 (setq-local font-lock-defaults '(scss-font-lock-keywords nil t)))
552
553 (provide 'css-mode)
554 ;;; css-mode.el ends here