From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Karl Chen Newsgroups: gmane.emacs.devel Subject: Re: css-mode Date: Tue, 17 Jan 2006 03:39:38 -0800 Message-ID: References: <878xtkghxj.fsf-monnier+emacs@gnu.org> Reply-To: quarl+dated+1137929880.5e73d2@nospam.quarl.org NNTP-Posting-Host: main.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1137514673 30810 80.91.229.2 (17 Jan 2006 16:17:53 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Tue, 17 Jan 2006 16:17:53 +0000 (UTC) Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jan 17 17:17:45 2006 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by ciao.gmane.org with esmtp (Exim 4.43) id 1EytV0-0008RA-G0 for ged-emacs-devel@m.gmane.org; Tue, 17 Jan 2006 17:15:59 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1EytUU-0004Pq-Qr for ged-emacs-devel@m.gmane.org; Tue, 17 Jan 2006 11:15:27 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Eyr7P-0005oU-FA for emacs-devel@gnu.org; Tue, 17 Jan 2006 08:43:28 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1EyppF-0000cH-Mp for emacs-devel@gnu.org; Tue, 17 Jan 2006 07:22:29 -0500 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1EypEW-00071p-U8 for emacs-devel@gnu.org; Tue, 17 Jan 2006 06:42:41 -0500 Original-Received: from [80.91.229.2] (helo=ciao.gmane.org) by monty-python.gnu.org with esmtp (TLS-1.0:RSA_AES_128_CBC_SHA:16) (Exim 4.34) id 1EypIC-0007Zt-BX for emacs-devel@gnu.org; Tue, 17 Jan 2006 06:46:28 -0500 Original-Received: from list by ciao.gmane.org with local (Exim 4.43) id 1EypBt-0000k7-ID for emacs-devel@gnu.org; Tue, 17 Jan 2006 12:39:57 +0100 Original-Received: from roar.cs.berkeley.edu ([128.32.35.215]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Tue, 17 Jan 2006 12:39:57 +0100 Original-Received: from quarl by roar.cs.berkeley.edu with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Tue, 17 Jan 2006 12:39:57 +0100 X-Injected-Via-Gmane: http://gmane.org/ Original-To: emacs-devel@gnu.org Original-Lines: 432 Original-X-Complaints-To: usenet@sea.gmane.org X-Gmane-NNTP-Posting-Host: roar.cs.berkeley.edu X-Quack-Archive: 1 Gmane-From: 1 User-Agent: Gnus/5.110004 (No Gnus v0.4) Emacs/22.0.50 (gnu/linux) Cancel-Lock: sha1:yJmM29V1r1AE7iQyb+FMlnZ085U= X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:49208 Archived-At: Here is the one I've been using. I don't care whose version is initially used; I'm sure each has features to be merged. It just goes to show that emacs desperately needs to distribute a CSS mode since everyone has their own. ;;; css-mode.el ;; css-mode.el -*- Emacs-Lisp -*- ;; Mode for editing Cascading Style Sheets ;; Created: ;; Time-stamp: <2002-11-25 10:21:39 foof> ;; Author: Alex Shinn ;; Version: 0.3 ;; Keywords: html, style sheets, languages ;; Copyright (C) 2005 Karl Chen ;; Copyright (C) 2000-2002 Alex Shinn ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 2 of ;; the License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public ;; License along with this program; if not, write to the Free ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, ;; MA 02111-1307 USA ;;; Commentary: ;; ;; This file provides a major mode for editing level 1 and 2 Cascading ;; Style Sheets. It offers syntax highlighting, indentation, and ;; auto-completion of various CSS elements. ;; ;; To use it, put the following in your .emacs: ;; ;; (autoload 'css-mode "css-mode" "Mode for editing CSS files" t) ;; ;; You may also want something like: ;; ;; (setq auto-mode-alist ;; (append '(("\\.css$" . css-mode)) ;; auto-mode-alist)) ;; ;;; ChangeLog: ;; ;; 2002/11/25 (version 0.3): ;; * changed to use indent-to to obey tab preference (Vasily Korytov) ;; 2005-11-21 Karl Chen ;; * Major cleanup (defgroup css nil "Customizations for editing Cascading Style Sheets" :group 'languages) (defcustom css-electric-semi-behavior nil "If non-nil semicolons are electric in css mode" :group 'css :type 'boolean) (defcustom css-electric-brace-behavior nil "If non-nil braces are electric in css mode" :group 'css :type 'boolean) (defcustom css-indent-offset 4 "Number of spaces to indent lines in CSS mode" :group 'css :type 'integer) (defcustom css-tab-mode 'auto "Behavior of tab in CSS mode" :group 'css :type '(choice (const :tag "Always insert" insert) (const :tag "Always indent" indent) (const :tag "Always complete" complete) (const :tag "Auto" auto) )) (defun css--list-to-table (input) (let ((table (make-vector 5 0))) (mapcar (lambda (x) (intern x table)) input) table)) (defvar css-at-rule-keywords '("import" "media" "page" "font-face" "charset") "Keywords for CSS at rules" ) (defvar css-at-rule-table (css--list-to-table css-at-rule-keywords) "Table for CSS at rules") (defvar css-element-keywords '("A" "ADDRESS" "B" "BLOCKQUOTE" "BODY" "BR" "CITE" "CODE" "DIR" "DIV" "DD" "DL" "DT" "EM" "FORM" "H1" "H2" "H3" "H4" "H5" "H6" "HR" "I" "IMG" "KBD" "LI" "MENU" "OL" "P" "PRE" "SAMP" "SPAN" "STRONG" "TABLE" "TR" "TH" "TD" "TT" "UL" "VAR" ) "Common CSS elements" ) (defvar css-element-table (css--list-to-table css-element-keywords) "Table for CSS elements") (defvar css-property-keywords '("azimuth" "background" "background-attachment" "background-color" "background-image" "background-position" "background-repeat" "border" "border-collapse" "border-color" "border-spacing" "border-style" "border-top" "border-right" "border-bottom" "border-left" "border-top-color" "border-right-color" "border-bottom-color" "border-left-color" "border-top-style" "border-right-style" "border-bottom-style" "border-left-style" "border-top-width" "border-right-width" "border-bottom-width" "border-left-width" "border-width" "bottom" "caption-side" "clear" "clip" "color" "content" "counter-increment" "counter-reset" "cue" "cue-after" "cue-before" "cursor" "direction" "display" "elevation" "empty-cells" "float" "font" "font-family" "font-size" "font-size-adjust" "font-stretch" "font-style" "font-variant" "font-weight" "height" "left" "letter-spacing" "line-height" "list-style" "list-style-image" "list-style-position" "list-style-type" "margin" "margin-top" "margin-right" "margin-bottom" "margin-left" "marker-offset" "marks" "max-height" "max-width" "min-height" "min-width" "orphans" "outline" "outline-color" "outline-style" "outline-width" "overflow" "padding" "padding-top" "padding-right" "padding-bottom" "padding-left" "page" "page-break-after" "page-break-before" "page-break-inside" "pause" "pause-after" "pause-before" "pitch" "pitch-range" "play-during" "position" "quotes" "richness" "right" "size" "speak" "speak-header" "speak-numeral" "speak-punctuation" "speech-rate" "stress" "table-layout" "text-align" "text-decoration" "text-indent" "text-shadow" "text-transform" "top" "unicode-bidi" "vertical-align" "visibility" "voice-family" "volume" "white-space" "widows" "width" "word-spacing" "z-index") "CSS properties") (defvar css-property-table (css--list-to-table css-property-keywords) "Table for CSS properties") (defconst css-font-lock-keywords (purecopy (let* ((css-keywords "\\(url\\|![ \t]*important\\)") (css-ident "[a-zA-Z][a-zA-Z0-9-]*") (css-at-rule (concat "\\(@" (regexp-opt css-at-rule-keywords t) "\\)")) (css-element-s (concat "^\\(" css-ident "\\)")) (css-element (concat "\\(?:[,+>][ \t]*\\)\\(" css-ident "\\)")) (css-class (concat css-element "?\\.\\(" css-ident "\\)")) (css-pseudo (concat ":\\(" css-ident "\\)")) (css-attr (concat "\\[\\(" css-ident "\\)\\]")) (css-id (concat "#\\(" css-ident "\\)")) (css-declaration (concat "[ \t][ \t]*\\(\\<" css-ident "\\>\\):")) ) (list (list css-keywords 1 'font-lock-keyword-face) (list css-at-rule 1 'font-lock-keyword-face) (list css-element-s 1 'font-lock-function-name-face) (list css-element 1 'font-lock-function-name-face) (list css-class 2 'font-lock-type-face) (list css-pseudo 1 'font-lock-constant-face) (list css-attr 1 'font-lock-variable-name-face) (list css-id 1 'font-lock-string-face) (list css-declaration 1 'font-lock-variable-name-face) ))) "Expressions for highlighting in css-mode.") (defvar css-mode-syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?+ "." table) (modify-syntax-entry ?= "." table) (modify-syntax-entry ?< "." table) (modify-syntax-entry ?> "." table) (modify-syntax-entry ?- "w" table) (modify-syntax-entry ?/ "w" table) (modify-syntax-entry ?. "w" table) (modify-syntax-entry ?\' "\"" table) ;; (modify-syntax-entry ?/ ". 1456" table) ; XEmacs(?) (modify-syntax-entry ?/ ". 124b" table) ; GNU Emacs (modify-syntax-entry ?* ". 23" table) (modify-syntax-entry ?\n "> b" table) ;; Give CR the same syntax as newline, for selective-display (modify-syntax-entry ?\^m "> b" table) table) "Syntax table used in `css-mode' buffers.") (defvar css-mode-map (let ((map (make-sparse-keymap))) (define-key map ";" 'css-electric-semicolon) (define-key map "{" 'css-electric-brace) (define-key map "}" 'css-electric-brace) (define-key map "\t" 'css-tab-function) (define-key map "\C-c\C-c" 'css-comment-region) (define-key map "\C-c\C-a" 'css-complete-at-keyword) (define-key map "\C-c\C-e" 'css-complete-element) (define-key map "\C-c\C-p" 'css-complete-property) map) "Keymap used in `css-mode' buffers.") ;;; Utility functions (defun css-in-comment-p () "Returns non-nil if point is within a comment." (save-excursion (let ((here (point))) (and (search-backward "/*" nil t) (not (search-forward "*/" here t)))))) (defun css-complete-symbol (&optional table predicate prettify) (let* ((end (point)) (beg (save-excursion (skip-syntax-backward "w") (point))) (pattern (buffer-substring beg end)) (table (or table obarray)) (completion (try-completion pattern table predicate))) (cond ((eq completion t)) ((null completion) (error "Can't find completion for \"%s\"" pattern)) ((not (string-equal pattern completion)) (delete-region beg end) (insert completion)) (t (message "Making completion list...") (let ((list (all-completions pattern table predicate))) (if prettify (setq list (funcall prettify list))) (with-output-to-temp-buffer "*Help*" (display-completion-list list))) (message "Making completion list...%s" "done"))))) (defsubst css--backup-to-nonempty-line () (while (and (= 0 (forward-line -1)) (or (looking-at "^[ \t]*$") (css-in-comment-p) )) ;; Jump to a non comment/white-space line )) (defun css--indent-depth () (let ((depth 0)) (save-excursion (css--backup-to-nonempty-line) (cond ((looking-at "\\([ \t]*\\)\\([^ \t].*\\)?{[ \t]*$") (setq depth (+ (- (match-end 1) (match-beginning 1)) css-indent-offset ))) ((looking-at "\\([ \t]*\\)[^ \t]") (setq depth (- (match-end 1) (match-beginning 1))) ) (t (setq depth 0)))) (save-excursion (beginning-of-line) (if (looking-at "[ \t]*}") (setq depth (- depth css-indent-offset)))) depth)) (defun indent-line-to-excursion (column) (setq column (max column 0)) (if (> (current-column) (current-indentation)) (save-excursion (indent-line-to column)) (indent-line-to column))) (defun css-indent-line () "Indent the current line" (unless (or (css-in-comment-p) (looking-at "[ \t]*/\\*")) (indent-line-to-excursion (css--indent-depth)))) ;;; Commands (defun css-electric-semicolon (arg) "Insert a semi-colon, and possibly indent line. If numeric argument is not given, or is 1, auto-indent according to `css-electric-semi-behavior'. If arg is 0, do not auto-indent, if arg is 2 always auto-indent, and if arg is anything else invert the usual behavior." (interactive "P") ;; insert a semicolon (self-insert-command 1) ;; maybe do electric behavior (or (css-in-comment-p) (and (eq arg 1) css-electric-semi-behavior (css-indent-line) ) (and (eq arg 2) (css-indent-line) ) (eq arg 0) (or (not css-electric-semi-behavior) (css-indent-line) ))) (defun css-electric-brace (arg) "Insert a brace, and possibly indent line. If numeric argument is not given, or is 1, auto-indent according to `css-electric-brace-behavior'. If arg is 0, do not auto-indent, if arg is 2 always auto-indent, and if arg is anything else invert the usual behavior." (interactive "P") ;; insert a brace (self-insert-command 1) ;; maybe do electric behavior (or (css-in-comment-p) (and (eq arg 1) css-electric-brace-behavior (css-indent-line) ) (and (eq arg 2) (css-indent-line) ) (eq arg 0) (or (not css-electric-brace-behavior) (css-indent-line) ))) (defun css-complete-at-keyword () "Complete the standard element at point" (interactive) (let ((completion-ignore-case t)) (css-complete-symbol css-at-rule-table) )) (defun css-complete-element () "Complete the standard element at point" (interactive) (let ((completion-ignore-case t)) (css-complete-symbol css-element-table) )) (defun css-complete-property () "Complete the standard element at point" (interactive) (let ((completion-ignore-case t)) (css-complete-symbol css-property-table) )) (defun css-tab-function (&optional arg) "Function to call when tab is pressed in CSS mode. With a prefix arg, insert a literal tab. Otherwise behavior depends on the value of `css-tab-mode'. If it's 'insert, insert a literal tab. If it's 'indent, indent the current line, and if it's 'complete, try to complete the expression before point. A value of 'auto means to inspect the current line, and indent if point is at the beginning or end of the line, but complete if it's at a word. There are three possible completions to perform: `css-complete-at-keyword' if the point is after an '@', `css-complete-property' if point is inside a block, and `css-complete-element' otherwise." (interactive "P") (let* ((end (point)) (start (prog2 (beginning-of-line) (point) (goto-char end) )) (prefix (buffer-substring start end)) ) (cond ((or arg (eq css-tab-mode 'insert)) (insert "\t")) ((eq css-tab-mode 'indent) (css-indent-line)) ((and (not (eq css-tab-mode 'complete)) (or (string-match "^[ \t]*[{}]?[ \t]*$" prefix) (string-match "^.*;[ \t]*" prefix) )) ;; indent at the beginning or end of a line (css-indent-line)) ((string-match "^.*@[a-zA-Z0-9-]*$" prefix) (css-complete-at-keyword)) ((string-match "^\\([ \t]+.*\\|.*\{[ \t]*[a-zA-Z]+\\)$" prefix) ;; complete properties on non-starting lines (css-complete-property)) ;; otherwise try an element (t (css-complete-element)) ))) ;;;###autoload (define-derived-mode css-mode fundamental-mode "CSS" "Major mode for editing CSS files" ;; local variables (make-local-variable 'parse-sexp-ignore-comments) (make-local-variable 'comment-start-skip) (make-local-variable 'comment-start) (make-local-variable 'comment-end) (make-local-variable 'block-comment-start) (make-local-variable 'block-comment-end) (make-local-variable 'block-comment-left) ;; (make-local-variable 'block-comment-right) ;; (make-local-variable 'block-comment-top-right) ;; (make-local-variable 'block-comment-bot-left) ;; (make-local-variable 'block-comment-char) (make-local-variable 'paragraph-start) (make-local-variable 'paragraph-separate) (make-local-variable 'paragraph-ignore-fill-prefix) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(css-font-lock-keywords)) ;; now set their values (setq parse-sexp-ignore-comments t comment-start-skip "/\\*+ *\\|// *" comment-start "/\\*" comment-end "\\*/") (setq block-comment-start "/*" block-comment-end "*/" ;; block-comment-left " * " ;; block-comment-right " *" ;; block-comment-top-right "" ;; block-comment-bot-left " " ;; block-comment-char ?* ) (setq indent-line-function 'css-indent-line indent-region-function 'css-indent-region paragraph-ignore-fill-prefix t paragraph-start (concat "\\|$" page-delimiter) paragraph-separate paragraph-start) ;; (run-hooks 'css-mode-hook) ) (provide 'css-mode) ;;; css-mode.el ends here. -- Karl 2006-01-17 03:38