1 ;;;; A major mode for editing CSS.
3 ;;; Adds font locking, some rather primitive indentation handling and
7 (defvar cssm-version "0.11"
8 "The current version number of `css-mode'.")
10 ;;; copyright (c) 1998 Lars Marius Garshol, larsga@ifi.uio.no
11 ;;; $Id: css-mode.el,v 1.9 2000/01/05 21:21:56 larsga Exp $
13 ;;; css-mode is free software; you can redistribute it and/or
14 ;;; modify it under the terms of the GNU General Public License
15 ;;; as published by the Free Software Foundation; either version 2
16 ;;; of the License, or (at your option) any later version.
18 ;;; css-mode is distributed in the hope that it will be useful,
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;;; GNU General Public License for more details.
23 ;;; You should have received a copy of the GNU General Public License
24 ;;; along with css-mode; if not, write to the Free Software
25 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 ; Send me an email if you want new features (or if you add them yourself).
28 ; I will do my best to preserve the API to functions not explicitly marked
29 ; as internal and variables shown as customizable. I make no promises about
32 ; Bug reports are very welcome. New versions of the package will appear at
33 ; http://www.garshol.priv.no/download/software/css-mode/
34 ; You can register at the same address if you want to be notified when a
35 ; new version appears.
37 ; Thanks to Philippe Le Hegaret, Kjetil Kjernsmo, Alf-Ivar Holm and
38 ; Alfred Correira for much useful feedback. Alf-Ivar Holm also contributed
41 ; This version should be up to date with the following:
42 ; CSS1: http://www.w3.org/TR/1999/REC-CSS1-19990111
43 ; CSS2: http://www.w3.org/TR/1998/REC-CSS2-19980512
45 ; To install, put this in your .emacs:
47 ; (autoload 'css-mode "css-mode")
48 ; (setq auto-mode-alist
49 ; (cons '("\\.css\\'" . css-mode) auto-mode-alist))
53 ; - must not color URL file name extensions as class selectors (*.css)
54 ; - color [] and url() constructs correctly, even if quoted strings present
55 ; - disregard anything inside strings
57 ;; Possible later additions:
59 ; - forward/backward style/@media rule commands
60 ; - more complete syntax table
70 ; Customizable variables:
73 "Major mode for editing CSS files."
78 (defcustom cssm-indent-level 2
79 "*The indentation level inside @media rules."
83 (defcustom cssm-mirror-mode t
84 "*Whether brackets, quotes etc should be mirrored automatically on insertion."
88 (defcustom cssm-newline-before-closing-bracket nil
89 "*In `cssm-mirror-mode', controls whether a newline should be inserted
90 before the closing bracket or not."
94 (defcustom cssm-indent-function #'cssm-c-style-indenter
95 "*Which function to use when deciding which column to indent to.
96 Built-in choices are `cssm-old-style-indenter' and `cssm-c-style-indenter'."
100 ; The rest of the code:
102 (defvar cssm-properties
107 "background-attachment" ; 1 2
108 "background-color" ; 1 2
109 "background-image" ; 1 2
110 "background-position" ; 1 2
111 "background-repeat" ; 1 2
115 "border-bottom" ; 1 2
116 "border-bottom-color" ; 2
117 "border-bottom-style" ; 2
118 "border-bottom-width" ; 1 2
120 "border-collapse" ; 2
122 "border-left-color" ; 2
123 "border-left-style" ; 2
124 "border-left-width" ; 1 2
126 "border-right-color" ; 2
127 "border-right-style" ; 2
128 "border-right-width" ; 1 2
132 "border-top-color" ; 2
133 "border-top-style" ; 2
134 "border-top-width" ; 1 2
139 ;"cell-spacing" ; 2 removed in PR-CSS2-19980324
144 ;"column-span" ; 2 removed in REC-CSS2-19980512
146 "counter-increment" ; 2
162 "font-size-adjust" ; 2
169 "letter-spacing" ; 1 2
172 "list-style-image" ; 1 2
173 "list-style-position" ; 1 2
174 "list-style-type" ; 1 2
176 "margin-bottom" ; 1 2
194 "padding-bottom" ; 1 2
196 "padding-right" ; 1 2
199 "page-break-after" ; 2
200 "page-break-before" ; 2
201 "page-break-inside" ; 2
213 ;"row-span" ; 2 removed in REC-CSS2-19980512
217 ;"speak-date" ; 2 removed in PR-CSS2-19980324
220 "speak-punctuation" ; 2
221 ;"speak-time" ; 2 removed in PR-CSS2-19980324
229 "text-decoration" ; 1 2
232 "text-transform" ; 1 2
238 "vertical-align" ; 1 2
250 "A list of all CSS properties and descriptors.")
252 (defvar cssm-properties-alist
253 (mapcar (lambda(prop)
254 (cons (concat prop ":") nil)) cssm-properties)
255 "An association list of the CSS properties for completion use.")
257 (defvar cssm-keywords
262 "!\\s-*important" ; 1 2
266 (mapcar (lambda(property)
267 (concat property "\\s-*:"))
270 "A list of all CSS keywords.")
272 (defvar cssm-pseudos '(
285 "A list of all CSS pseudo-classes and elements.")
288 (defun cssm-list-2-regexp(altlist)
289 "Takes a list and returns the regexp \\(elem1\\|elem2\\|...\\)"
290 (let ((regexp "\\("))
291 (mapcar (lambda(elem)
292 (setq regexp (concat regexp elem "\\|")))
294 (concat (substring regexp 0 -2) ; cutting the last "\\|"
298 (defvar cssm-font-lock-keywords
300 (cons (cssm-list-2-regexp cssm-keywords) font-lock-keyword-face)
301 (cons "\\.[a-zA-Z][-a-zA-Z0-9.]+" font-lock-variable-name-face)
302 (cons (concat ":" (cssm-list-2-regexp cssm-pseudos))
303 font-lock-variable-name-face)
304 (cons "#[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)?"
305 font-lock-reference-face)
306 (cons "\\[.*\\]" font-lock-variable-name-face)
307 (cons "#[-a-zA-Z0-9]*" font-lock-function-name-face)
308 (cons "rgb(\\s-*[0-9]+\\(\\.[0-9]+\\s-*%\\s-*\\)?\\s-*,\\s-*[0-9]+\\(\\.[0-9]+\\s-*%\\s-*\\)?\\s-*,\\s-*[0-9]+\\(\\.[0-9]+\\s-*%\\s-*\\)?\\s-*)"
309 font-lock-reference-face)
311 "Rules for highlighting CSS style sheets.")
313 (defvar cssm-mode-map ()
314 "Keymap used in CSS mode.")
315 (when (not cssm-mode-map)
316 (setq cssm-mode-map (make-sparse-keymap))
317 (define-key cssm-mode-map (read-kbd-macro "C-c C-c") 'cssm-insert-comment)
318 (define-key cssm-mode-map (read-kbd-macro "C-c C-u") 'cssm-insert-url)
319 (define-key cssm-mode-map (read-kbd-macro "}") 'cssm-insert-right-brace-and-indent)
320 (define-key cssm-mode-map (read-kbd-macro "M-TAB") 'cssm-complete-property))
322 ;;; Cross-version compatibility layer
324 (when (not (or (apropos-macrop 'kbd)
327 "Convert KEYS to the internal Emacs key representation.
328 KEYS should be a string constant in the format used for
329 saving keyboard macros (see `insert-kbd-macro')."
330 (read-kbd-macro keys)))
332 ;;; Auto-indentation support
335 (defun cssm-insert-right-brace-and-indent()
341 (defun cssm-inside-atmedia-rule()
342 "Decides if point is currently inside an @media rule."
343 (let ((orig-pos (point))
344 (atmedia (re-search-backward "@media" 0 t))
345 (balance 1) ; used to keep the {} balance, 1 because we start on a {
347 ; Going to the accompanying {
348 (re-search-forward "{" (point-max) t)
350 nil ; no @media before this point => not inside
351 (while (and (< (point) orig-pos)
353 (if (null (re-search-forward "[{}]" (point-max) 0))
354 (goto-char (point-max)) ; break
356 (if (string= (match-string 0) "{")
363 (defun cssm-rule-is-atmedia()
364 "Decides if point is currently on the { of an @media or ordinary style rule."
365 (let ((result (re-search-backward "[@}{]" 0 t)))
368 (string= (match-string 0) "@"))))
371 (defun cssm-find-column(first-char)
372 "Find which column to indent to."
374 ; Find out where to indent to by looking at previous lines
375 ; spinning backwards over comments
377 (while (and (setq pos (re-search-backward (cssm-list-2-regexp
378 '("/\\*" "\\*/" "{" "}"))
380 (string= (match-string 0) "*/"))
381 (search-backward "/*" (point-min) t))
383 ; did the last search find anything?
386 (let ((construct (match-string 0))
387 (column (current-column)))
388 (apply cssm-indent-function
390 ((string= construct "{")
392 ((cssm-rule-is-atmedia)
394 ((cssm-inside-atmedia-rule)
395 'inside-rule-and-atmedia)
398 ((string= construct "/*")
400 ((string= construct "}")
401 (if (cssm-inside-atmedia-rule)
408 (apply cssm-indent-function
413 (defun cssm-indent-line()
414 "Indents the current line."
417 (let* ((beg-of-line (point))
418 (pos (re-search-forward "[]@#a-zA-Z0-9;,.\"{}/*\n:[]" (point-max) t))
419 (first (match-string 0))
420 (start (match-beginning 0)))
422 (goto-char beg-of-line)
424 (let ((indent-column (cssm-find-column first)))
425 (goto-char beg-of-line)
427 ; Remove all leading whitespace on this line (
428 (if (not (or (null pos)
429 (= beg-of-line start)))
430 (kill-region beg-of-line start))
432 (goto-char beg-of-line)
435 (indent-to indent-column))))
437 ;;; Indent-style functions
439 (defun cssm-old-style-indenter(position column first-char-on-line)
440 "Old-style indentation for CSS buffers."
442 ((eq position 'inside-atmedia)
443 (if (string= "}" first-char-on-line)
447 ((eq position 'inside-rule)
450 ((eq position 'inside-rule-and-atmedia)
453 ((eq position 'inside-comment)
456 ((eq position 'outside)
459 (defun cssm-c-style-indenter(position column first-char-on-line)
460 "C-style indentation for CSS buffers."
462 ((or (eq position 'inside-atmedia)
463 (eq position 'inside-rule))
464 (if (string= "}" first-char-on-line)
468 ((eq position 'inside-rule-and-atmedia)
469 (if (string= "}" first-char-on-line)
471 (* 2 cssm-indent-level)))
473 ((eq position 'inside-comment)
476 ((eq position 'outside)
481 (define-skeleton cssm-insert-curlies
482 "Inserts a pair of matching curly parenthesises." nil
483 "{ " _ (if cssm-newline-before-closing-bracket "\n" " ")
486 (define-skeleton cssm-insert-quotes
487 "Inserts a pair of matching quotes." nil
490 (define-skeleton cssm-insert-parenthesises
491 "Inserts a pair of matching parenthesises." nil
494 (define-skeleton cssm-insert-comment
495 "Inserts a full comment." nil
498 (define-skeleton cssm-insert-url
502 (define-skeleton cssm-insert-brackets
503 "Inserts a pair of matching brackets." nil
506 (defun cssm-enter-mirror-mode()
507 "Turns on mirror mode, where quotes, brackets etc are mirrored automatically
510 (define-key cssm-mode-map (read-kbd-macro "{") 'cssm-insert-curlies)
511 (define-key cssm-mode-map (read-kbd-macro "\"") 'cssm-insert-quotes)
512 (define-key cssm-mode-map (read-kbd-macro "(") 'cssm-insert-parenthesises)
513 (define-key cssm-mode-map (read-kbd-macro "[") 'cssm-insert-brackets))
515 (defun cssm-leave-mirror-mode()
516 "Turns off mirror mode."
518 (define-key cssm-mode-map (read-kbd-macro "{") 'self-insert-command)
519 (define-key cssm-mode-map (read-kbd-macro "\"") 'self-insert-command)
520 (define-key cssm-mode-map (read-kbd-macro "(") 'self-insert-command)
521 (define-key cssm-mode-map (read-kbd-macro "[") 'self-insert-command))
523 ;;; Property completion
525 (defun cssm-property-at-point()
526 "If point is at the end of a property name: returns it."
528 (start (+ (re-search-backward "[^-A-Za-z]") 1)))
530 (buffer-substring start end)))
533 (defun cssm-maximum-common(alt1 alt2)
534 "Returns the maximum common starting substring of alt1 and alt2."
535 (let* ((maxlen (min (length alt1) (length alt2)))
536 (alt1 (substring alt1 0 maxlen))
537 (alt2 (substring alt2 0 maxlen)))
538 (while (not (string= (substring alt1 0 maxlen)
539 (substring alt2 0 maxlen)))
540 (setq maxlen (- maxlen 1)))
541 (substring alt1 0 maxlen)))
544 (defun cssm-common-beginning(alts)
545 "Returns the maximum common starting substring of all alts elements."
546 (let ((common (car alts)))
547 (dolist (alt (cdr alts) common)
548 (setq common (cssm-maximum-common alt common)))))
550 (defun cssm-complete-property-frame(completions)
551 ; This code stolen from message.el. Kudos to larsi.
552 (let ((cur (current-buffer)))
553 (pop-to-buffer "*Completions*")
554 (buffer-disable-undo (current-buffer))
555 (let ((buffer-read-only nil))
557 (let ((standard-output (current-buffer)))
558 (display-completion-list (sort completions 'string<)))
559 (goto-char (point-min))
560 (pop-to-buffer cur))))
562 (defun cssm-complete-property()
563 "Completes the CSS property being typed at point."
565 (let* ((prop (cssm-property-at-point))
566 (alts (all-completions prop cssm-properties-alist))
567 (proplen (length prop)))
568 (if (= (length alts) 1)
569 (insert (substring (car alts) proplen))
570 (let ((beg (cssm-common-beginning alts)))
571 (if (not (string= beg prop))
572 (insert (substring beg proplen))
574 (completing-read "Property: " cssm-properties-alist nil
580 "Major mode for editing CSS style sheets.
585 (kill-all-local-variables)
587 ; Setting up indentation handling
588 (make-local-variable 'indent-line-function)
589 (setq indent-line-function 'cssm-indent-line)
591 ; Setting up font-locking
592 (make-local-variable 'font-lock-defaults)
593 (setq font-lock-defaults '(cssm-font-lock-keywords nil t nil nil))
595 ; Setting up typing shortcuts
596 (make-local-variable 'skeleton-end-hook)
597 (setq skeleton-end-hook nil)
599 (when cssm-mirror-mode
600 (cssm-enter-mirror-mode))
602 (use-local-map cssm-mode-map)
604 ; Setting up syntax recognition
605 (make-local-variable 'comment-start)
606 (make-local-variable 'comment-end)
607 (make-local-variable 'comment-start-skip)
609 (setq comment-start "/* "
611 comment-start-skip "/\\*[ \n\t]+")
613 ; Setting up syntax table
614 (modify-syntax-entry ?* ". 23")
615 (modify-syntax-entry ?/ ". 14")
617 ; Final stuff, then we're done
618 (setq mode-name "CSS"
619 major-mode 'css-mode)
620 (run-hooks 'css-mode-hook))
622 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.css$" . css-mode))
626 ;; CSS-mode ends here