From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: E Sabof Newsgroups: gmane.emacs.bugs Subject: bug#13425: new css-mode indenter Date: Thu, 17 Jan 2013 00:17:34 +0000 Message-ID: References: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=20cf3074d594ecfdb804d370ebd7 X-Trace: ger.gmane.org 1358381927 19916 80.91.229.3 (17 Jan 2013 00:18:47 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 17 Jan 2013 00:18:47 +0000 (UTC) Cc: 13425@debbugs.gnu.org To: Stefan Monnier Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Thu Jan 17 01:19:01 2013 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1TvdC8-0000AA-Qc for geb-bug-gnu-emacs@m.gmane.org; Thu, 17 Jan 2013 01:19:01 +0100 Original-Received: from localhost ([::1]:42102 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TvdBr-0007Je-O7 for geb-bug-gnu-emacs@m.gmane.org; Wed, 16 Jan 2013 19:18:43 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:39201) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TvdBg-0007HJ-Ow for bug-gnu-emacs@gnu.org; Wed, 16 Jan 2013 19:18:40 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TvdBY-0006Sf-Qm for bug-gnu-emacs@gnu.org; Wed, 16 Jan 2013 19:18:32 -0500 Original-Received: from debbugs.gnu.org ([140.186.70.43]:58609) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TvdBY-0006Sb-Hg for bug-gnu-emacs@gnu.org; Wed, 16 Jan 2013 19:18:24 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.72) (envelope-from ) id 1TvdCA-0004Lp-3c for bug-gnu-emacs@gnu.org; Wed, 16 Jan 2013 19:19:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: E Sabof Original-Sender: debbugs-submit-bounces@debbugs.gnu.org Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 17 Jan 2013 00:19:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 13425 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 13425-submit@debbugs.gnu.org id=B13425.135838189416658 (code B ref 13425); Thu, 17 Jan 2013 00:19:02 +0000 Original-Received: (at 13425) by debbugs.gnu.org; 17 Jan 2013 00:18:14 +0000 Original-Received: from localhost ([127.0.0.1]:35841 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.72) (envelope-from ) id 1TvdBO-0004Kd-In for submit@debbugs.gnu.org; Wed, 16 Jan 2013 19:18:14 -0500 Original-Received: from mail-qa0-f49.google.com ([209.85.216.49]:40406) by debbugs.gnu.org with esmtp (Exim 4.72) (envelope-from ) id 1TvdBN-0004KW-2s for 13425@debbugs.gnu.org; Wed, 16 Jan 2013 19:18:13 -0500 Original-Received: by mail-qa0-f49.google.com with SMTP id r4so1686286qaq.1 for <13425@debbugs.gnu.org>; Wed, 16 Jan 2013 16:17:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:x-received:in-reply-to:references:date:message-id :subject:from:to:cc:content-type; bh=7OfVoUnfLRVRBNsicFQUhM5nQXgEcDN9Ug0AkhYfnhs=; b=i5JTfSoCxhsjloAIII3C3XWf3M3kk5oiP0miwXMCvUsmaE28wjrX2GfJe1moc+QGfz RZVyqmq5V4yVjfMOscCzQWJgio996tpciGsQDibqPvM8S+CEmr65YpUeL/QbEWyyO4JR y3keU/pLwQqItFVYDsSag+4Sd/jOKumdlt1EuLA9hJ4CHHi8q5QvmnJDPKLZO69bpf94 OTvMWJVNv0G5mzhlG7CZ8I2QTd23qyba6p7R1e1rfxGvLzm++uP4im2iEcxSyj7LtxYH LXpwLFlagdz1YH7wBB9r+nQkXGU8BaRNWmMyqa+UsbwltcQwT3XquXZd1uBqbfqd1h9P XsUw== X-Received: by 10.224.30.203 with SMTP id v11mr3790616qac.31.1358381854817; Wed, 16 Jan 2013 16:17:34 -0800 (PST) Original-Received: by 10.49.4.102 with HTTP; Wed, 16 Jan 2013 16:17:34 -0800 (PST) In-Reply-To: X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.13 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:69891 Archived-At: --20cf3074d594ecfdb804d370ebd7 Content-Type: text/plain; charset=ISO-8859-1 I've rewritten the indenter - this time I've added a parser for statements within curly brackets, so there is a lot less hackery. I've also added some more tests cases. Evgeni New css-mode.css diff --git a/css-mode.css b/css-mode.css new file mode 100644 index 0000000..f101971 --- /dev/null +++ b/css-mode.css @@ -0,0 +1,230 @@ +#top_menu .button.active, +#top_menu .button.active:active { + background-image: url('images/header_button_active.png'); + cursor: default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Comment */ +} + +#top_menu .button.active, +#top_menu .button.active:active +{ + /* Comment */ + background-image: url('images/header_button_active.png'); + cursor: default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Multiline + comment1 */ + /* Multiline + * comment2 */ +} +/* Multiline + comment1 */ +/* Multiline + * comment2 */ +#glass { + z-index: 2; + position: absolute; + top: -112px; + left: 0; + right: 0; + margin: 0 0 0 0; + margin: + 0 0 0 0; + text-shadow: + 1px 1px #FDE31D, + -1px -1px #B36300; + height: 140px; + background-image: url('images/honey_blurred2.png'); + background-image: url( + 'images/honey_blurred2.png'); + background-image: + #fff, + url('images/honey_blurred2.png'), + url('images/honey_blurred2.png'); + background-image: + #fff, + /* #fff, */ + url('images/honey_blurred2.png'); + background-image: #fff, + url('images/honey_blurred2.png'); + -webkit-mask-image: + -webkit-gradient( + linear, + left top, + /* left bottom, */ + left bottom, + color-stop( + 1, + rgba(0,0,0,0.2) + ), + color-stop(1, + + rgba(0,0,0,0.2) + ), + /* comment */ + color-stop(0.7, rgba(0,0,0,.0)), + color-stop + ( + 0.7, rgba(0,0,0,.0) + + ) + /* comment*/ ); + background-repeat: repeat-x; + background-position: center top; + background-attachment: fixed; } + +#glass { + margin: + /* 0 0 0 0; */ + /* text-shadow: */ + 1px 1px #FDE31D, + -1px -1px #B36300; + height: 140px; + background-image: url('images/honey_blurred2.png'); + /* background-image: url( */ + /* 'images/honey_blurred2.png'); */ + + background-image: + #fff, + url('images/honey_blurred2.png'), + url('images/honey_blurred2.png'); + background-image: + #fff, + /* #fff, */ + /* url('images/honey_blurred2.png'); */ + /* background-image: #fff, */ + url('images/honey_blurred2.png'); + -webkit-mask-image: + -webkit-gradient( + linear, + left top, + /* left bottom, */ + left bottom, + color-stop( + /* 1, */ + /* rgba(0,0,0,0.2) */ + ), + color-stop(1, + /* com */ + rgba(0,0,0,0.2) + + ), + /* comment */ + color-stop(0.7, rgba(0,0,0,.0)), + color-stop + ( + + 0.7, rgba(0,0,0,.0) + ) + /* comment*/); + -webkit-mask-image: + /* -webkit- */gradient( + linear, + left /* top */, + /* left */bottom, + left bottom, + color-stop( + /* 1, */ + /* rgba(0,0,0,0.2) */ + ), + color-stop(1, + /* com */ + rgba(0,0,0,0.2) + + ), + /* comment */ + color-stop(0.7/* , rgba(0,0,0,.0) */ + , rgba(0,0,0,.0) ), + color-stop + ( + + 0.7, rgba(0,0,0,.0) + ) + /* comment*/); + color: black, + /* red */, + blue; + /* -webkit-mask-image: */ + background: + -webkit-gradient( + /* com */ + ); + -webkit-mask-image: -webkit-gradient( + /* Forgivable? Better? */ + ), + -webkit-mask-image( + /* Back on track */ + ); + background-attachment: fixed + /* sdfsdf */ +} + +p:nth-child(2) { + margin: + 0 + 0 + 0 + 0; + margin: 0 0 + 0 + 0; + + /* comment */ + + text-shadow:1px 1px #FDE31D, /* no space */ + -1px -1px #B36300; +} + +p:nth-child +( + 2 +) +{ + height: 2px +} + +#field_message { + width: 100%; + display: block; + -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ + -moz-box-sizing: border-box; /* Firefox, other Gecko */ + box-sizing: border-box; /* Opera/IE 8+ */ + /* resize: vertical; */ + resize: none; + height: 160px +} + + +some { + +} + +/* SASS */ + +.some { + /* resize: vertical; */ + resize: none; + + height: 160px; + .other, + .another, + &:a, + #id { + /* resize: vertical; */ + resize: none; + + height: 160px; + } + /* Comment */ + resize: none; + height: 160px; + /* Comment */ +} + + +/* Local Variables: */ +/* css-debug: t */ +/* End: */ \ No newline at end of file Modified css-mode.el diff --git a/css-mode.el b/css-mode.el index 1abe9a8..9f3a786 100644 --- a/css-mode.el +++ b/css-mode.el @@ -31,12 +31,95 @@ ;; - completion ;; - fix font-lock errors with multi-line selectors +;;; TODO: +;; extra empty line indentation + ;;; Code: +(require 'cl-lib) + (defgroup css nil "Cascading Style Sheets (CSS) editing mode." :group 'languages) +;; Debugging + +(defvar css-debug-overlays nil) + +(defun css-debug-overlay (beginning end color) + (let ((overlay (make-overlay beginning end))) + (overlay-put overlay 'face `(:background ,color)) + (push overlay css-debug-overlays))) + +(defvar css-debug nil) + +(defun css-debug-msg (name) + (when css-debug + (message "%s" name))) + +(defun css-debug-goto-root-declaration () + (let* (( ppss (syntax-ppss)) + ( depth (nth 0 ppss))) + (when (nth 3 ppss) + (goto-char (nth 8 ppss))) + (while (and (cl-plusp depth) + (not (equal (char-after) ?\{ ))) + (up-list -1) + (cl-decf depth)))) + +(defun css-debug-parser-current () + (interactive) + (mapc 'delete-overlay css-debug-overlays) + (let* (( point + (save-excursion + (back-to-indentation) + (point))) + ( parsed + (save-excursion + (css-debug-goto-root-declaration) + (css-parse-curly))) + ( relevant + (css-pc-get-relevant parsed point))) + (css-debug-overlay + (car relevant) + (cadr relevant) + "DarkRed") + nil)) + +(defun css-debug-highight-parsed (parsed) + (mapc 'delete-overlay css-debug-overlays) + (cl-mapc (lambda (item) + (css-debug-overlay + (car item) + (cadr item) + (case (caddr item) + ('nested-selector "DarkGreen") + ('comment "SaddleBrown") + ( t "DarkRed")))) + parsed)) + +(defun css-debug-parser-all () + (interactive) + (let* (( parsed + (save-excursion + (css-debug-goto-root-declaration) + (css-parse-curly)))) + (css-debug-highight-parsed + parsed) + nil)) + +(defun css-debug-parser-inside () + (interactive) + (let* (( parsed + (save-excursion + (css-debug-goto-root-declaration) + (css-parse-curly))) + ( inside + (css-pc-inside-statement + parsed (point)))) + (message "P %s" inside))) + +;; EOF Debugging (defun css-extract-keyword-list (res) (with-temp-buffer @@ -92,7 +175,6 @@ (t nil))) elems)) - (defun css-extract-props-and-vals () (with-temp-buffer (url-insert-file-contents "http://www.w3.org/TR/CSS21/propidx.html") @@ -122,7 +204,7 @@ (defconst css-pseudo-ids '("active" "after" "before" "first" "first-child" "first-letter" "first-line" - "focus" "hover" "lang" "left" "link" "right" "visited") + "focus" "hover" "lang" "last-child" "left" "link" "right" "visited") "Identifiers for pseudo-elements and pseudo-classes.") (defconst css-at-ids @@ -264,8 +346,8 @@ '(css-font-lock-keywords nil t)) ;;;###autoload -(define-derived-mode css-mode fundamental-mode "CSS" - "Major mode to edit Cascading Style Sheets." +(define-derived-mode css-mode fundamental-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]*") @@ -276,6 +358,8 @@ (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) + (setq-local beginning-of-defun-function 'css-beginning-of-defun) + (setq-local end-of-defun-function 'css-end-of-defun) (when css-electric-keys (let ((fc (make-char-table 'auto-fill-chars))) (set-char-table-parent fc auto-fill-chars) @@ -394,102 +478,188 @@ ;; FIXME: We should also skip punctuation. (not (memq (char-after) '(?\; ?\}))))))))))) -(defun css-indent-calculate-virtual () - (if (or (save-excursion (skip-chars-backward " \t") (bolp)) - (if (looking-at "\\s(") - (save-excursion - (forward-char 1) (skip-chars-forward " \t") - (not (or (eolp) (looking-at comment-start-skip)))))) - (current-column) - (css-indent-calculate))) - -(defcustom css-indent-offset 4 - "Basic size of one indentation step." - :version "22.2" - :type 'integer - :group 'css) +(defun css-beginning-of-defun (&optional arg) + (unless arg (setq arg 1)) + (when (progn + ;; What for? + (unless (zerop (current-column)) + (end-of-line)) + (re-search-backward "^[^\n ].+{[ ]?$" nil t arg)) + (while (save-excursion + (and (zerop (forward-line -1)) + (string-match-p + "^[^}[:space:]/]" + (buffer-substring + (line-beginning-position) + (line-end-position))))) + (forward-line -1)))) + +(defun css-end-of-defun (&optional arg) + (interactive) + (unless arg (setq arg 1)) + (ignore-errors + (when (cl-plusp (car (syntax-ppss))) + (css-beginning-of-defun)) + (progn + (search-forward "{" nil t arg) + (backward-char) + (forward-sexp) + (ignore-errors + (forward-char))) + t)) + +(defun css-go-up () + (let* (( ppss (syntax-ppss))) + (when (or (nth 3 ppss) (nth 4 ppss)) + (goto-char (nth 8 ppss))) + (when (cl-plusp (nth 0 ppss)) + (up-list -1)))) + +(defmacro css-while-point-moving (&rest rest) + (let ((old-point (cl-gensym))) + `(let (,old-point) + (while (not (equal (point) ,old-point)) + (setq ,old-point (point)) + ,@rest)))) + +(defun css-parse-curly () + (let (( start (point)) + ( indentation (current-indentation)) + ( end (save-excursion + (forward-sexp) + (point))) + point result) + (forward-char) + (cl-loop named main-loop + do + (skip-chars-forward "\n\t " end) + (when (>= (point) (1- end)) + (cl-return-from main-loop)) + (setq point (point)) + (if (forward-comment 1) + (push (list point (point) 'comment) result) + (progn + (cl-loop (unless (re-search-forward ";\\|{\\|}" end t) + (cl-return-from main-loop)) + (unless (nth 4 (syntax-ppss)) + (cl-return))) + (cond ( (equal (char-before) ?\{ ) + (backward-char) + (forward-sexp) + (push (list point (point) 'nested-selector) result)) + ( (equal (char-before) ?\} ) + (backward-char) + (css-while-point-moving + (skip-chars-backward "\n\t " start) + (forward-comment -1)) + (push (list point (point) 'statement) result)) + ( t (push (list point (point) 'statement) result)))))) + (nreverse result))) + +(defun css-pc-get-relevant (parsed point) + (car (reverse (cl-remove-if (apply-partially '< point) + parsed :key 'car)))) + +(defun css-pc-inside-statement (parsed point) + (cl-some (lambda (item) + (and (<= (car item) point) + (<= point (cadr item)))) + parsed)) (defun css-indent-calculate () - (let ((ppss (syntax-ppss)) - pos) - (with-syntax-table css-navigation-syntax-table - (save-excursion - (cond - ;; Inside a string. - ((nth 3 ppss) 'noindent) - ;; Inside a comment. - ((nth 4 ppss) - (setq pos (point)) - (forward-line -1) - (skip-chars-forward " \t") - (if (>= (nth 8 ppss) (point)) - (progn - (goto-char (nth 8 ppss)) - (if (eq (char-after pos) ?*) - (forward-char 1) - (if (not (looking-at comment-start-skip)) - (error "Internal css-mode error") - (goto-char (match-end 0)))) - (current-column)) - (if (and (eq (char-after pos) ?*) (eq (char-after) ?*)) - (current-column) - ;; 'noindent - (current-column) - ))) - ;; In normal code. - (t - (or - (when (looking-at "\\s)") - (forward-char 1) - (backward-sexp 1) - (css-indent-calculate-virtual)) - (when (looking-at comment-start-skip) - (forward-comment (point-max)) - (css-indent-calculate)) - (when (save-excursion (forward-comment (- (point-max))) - (setq pos (point)) - (eq (char-syntax (preceding-char)) ?\()) - (goto-char (1- pos)) - (if (not (looking-at "\\s([ \t]*")) - (error "Internal css-mode error") - (if (or (memq (char-after (match-end 0)) '(?\n nil)) - (save-excursion (goto-char (match-end 0)) - (looking-at comment-start-skip))) - (+ (css-indent-calculate-virtual) css-indent-offset) - (progn (goto-char (match-end 0)) (current-column))))) - (progn - (css-backward-sexp 1) - (if (looking-at "\\s(") - (css-indent-calculate) - (css-indent-calculate-virtual)))))))))) - + (save-match-data + (condition-case error + (with-syntax-table css-navigation-syntax-table + (back-to-indentation) + (let* ((point (point)) + (ppss (syntax-ppss)) + ( css-curly-parsed + (save-excursion + (css-go-up) + (when (equal (char-after) ?{ ) + (css-parse-curly)))) + css-parsed-relevant + (block-ending-line + (member (char-after + (save-excursion + (back-to-indentation) + (point))) + '( ?\} ?\) ) ))) + (cond ( (nth 4 ppss) + ;; Inside a multiline comment + (css-debug-msg "MC") + (save-excursion + (forward-line -1) + (skip-chars-forward " \t") + (if (>= (nth 8 ppss) (point)) + (progn + (goto-char (nth 8 ppss)) + (if (eq (char-after point) ?*) + (forward-char 1) + (if (not (looking-at comment-start-skip)) + (error "Internal css-mode error") + (goto-char (match-end 0)))) + (current-column)) + (current-column)))) + ( ;; If "outside" indent to 0 + (zerop (nth 0 ppss)) + (css-debug-msg "ZERO") + 0) + ( ;; inside curly brackets + (and css-curly-parsed + (not block-ending-line) + (setq css-parsed-relevant + (css-pc-get-relevant + css-curly-parsed point)) + (not (eq (nth 2 css-parsed-relevant) + 'nested-selector))) + (css-debug-msg "C") + (+ css-indent-offset + (save-excursion + (css-go-up) + (current-indentation)) + (if (and (not (equal (line-number-at-pos + (car css-parsed-relevant)) + (line-number-at-pos))) + (css-pc-inside-statement + css-curly-parsed point)) + css-indent-offset 0))) + ( ;; Inside parentheses, closing brackets + t + (css-debug-msg "P") + (+ (save-excursion + (css-go-up) + (current-indentation)) + (if block-ending-line + 0 css-indent-offset)))))) + (error ;; My best error-less guess + (css-debug-msg "Err") + (* (car (syntax-ppss)) + css-indent-offset))))) (defun css-indent-line () "Indent current line according to CSS indentation rules." (interactive) - (let* ((savep (point)) - (forward-sexp-function nil) - (indent (condition-case nil - (save-excursion - (forward-line 0) - (skip-chars-forward " \t") - (if (>= (point) savep) (setq savep nil)) - (css-indent-calculate)) - (error nil)))) - (if (not (numberp indent)) 'noindent - (if savep - (save-excursion (indent-line-to indent)) - (indent-line-to indent))))) + (save-excursion + (indent-line-to (css-indent-calculate))) + (when (< (current-column) (current-indentation)) + (back-to-indentation))) + +(defcustom css-indent-offset 4 + "Basic size of one indentation step." + :version "22.2" + :type 'integer + :group 'css) (defun css-current-defun-name () "Return the name of the CSS section at point, or nil." (save-excursion (let ((max (max (point-min) (- (point) 1600)))) ; approx 20 lines back (when (search-backward "{" max t) - (skip-chars-backward " \t\r\n") - (beginning-of-line) - (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") - (match-string-no-properties 1)))))) + (skip-chars-backward " \t\r\n") + (beginning-of-line) + (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") + (match-string-no-properties 1)))))) (provide 'css-mode) ;;; css-mode.el ends here \ No newline at end of file On Tue, Jan 15, 2013 at 7:32 PM, E Sabof wrote: > Here is a patch, including a more advanced test case. In the end there is > a "failing" section - although I haven't seen anyone breaking at spaces. > > There is a varaiable called css-colon - the default is ":". If set to ": > ", it will also indent statements like this in SASS (a CSS pre-processor) > > a { &:hover, &:active { background: green; } } > > However, it will no longer correctly indent statements like this: > > background:#000, /* no space */ url('image.png'); > > If I manage to fix the "breaking at spaces" case, I'll send another patch. > > Evgeni > > New css-mode.css diff --git a/css-mode.css b/css-mode.css new file mode > 100644 index 0000000..40a732f --- /dev/null +++ b/css-mode.css @@ -0,0 > +1,86 @@ +#top_menu .button.active, +#top_menu .button.active:active { + > background-image: url('images/header_button_active.png'); + cursor: > default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Comment */ +} > +#top_menu .button.active, +#top_menu .button.active:active +{ + /* Comment > */ + background-image: url('images/header_button_active.png'); + cursor: > default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Multiline + > comment1 */ + /* Multiline + * comment2 */ +} +/* Multiline + comment1 */ > +/* Multiline + * comment2 */ +#glass { + z-index: 2; + position: absolute; > + top: -112px; + left: 0; + right: 0; + margin: 0 0 0 0; + margin: + 0 0 0 > 0; + text-shadow: + 1px 1px #FDE31D, + -1px -1px #B36300; + height: 140px; > + background-image: url('images/honey_blurred2.png'); + background-image: > url( + 'images/honey_blurred2.png'); + background-image: + #fff, + > url('images/honey_blurred2.png'), + url('images/honey_blurred2.png'); + > background-image: + #fff, + /* #fff, */ + url('images/honey_blurred2.png'); > + background-image: #fff, + url('images/honey_blurred2.png'); + > -webkit-mask-image: + -webkit-gradient( + linear, + left top, + /* left > bottom, */ + left bottom, + color-stop( + 1, + rgba(0,0,0,0.2) + ), + > color-stop(1, + rgba(0,0,0,0.2) + ), + /* comment */ + color-stop(0.7, > rgba(0,0,0,.0)), + color-stop + ( + 0.7, rgba(0,0,0,.0) + ) + /* comment*/ > ); + background-repeat: repeat-x; + background-position: center top; + > background-attachment: fixed; } + +#failing { + margin: 0 + 0 + 0 + 0; + > margin: + 0 + 0 + 0 + 0; +} \ No newline at end of file Modified > css-mode.el diff --git a/css-mode.el b/css-mode.el index 1abe9a8..44682e1 > 100644 --- a/css-mode.el +++ b/css-mode.el @@ -33,6 +33,8 @@ ;;; Code: > +(require 'cl-lib) + (defgroup css nil "Cascading Style Sheets (CSS) > editing mode." :group 'languages) @@ -92,7 +94,6 @@ (t nil))) elems)) - > (defun css-extract-props-and-vals () (with-temp-buffer > (url-insert-file-contents "http://www.w3.org/TR/CSS21/propidx.html") @@ > -122,7 +123,7 @@ (defconst css-pseudo-ids '("active" "after" "before" > "first" "first-child" "first-letter" "first-line" - "focus" "hover" "lang" > "left" "link" "right" "visited") + "focus" "hover" "lang" "last-child" > "left" "link" "right" "visited") "Identifiers for pseudo-elements and > pseudo-classes.") (defconst css-at-ids @@ -264,8 +265,8 @@ > '(css-font-lock-keywords nil t)) ;;;###autoload -(define-derived-mode > css-mode fundamental-mode "CSS" - "Major mode to edit Cascading Style > Sheets." +(define-derived-mode css-mode fundamental-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]*") @@ -276,6 +277,8 @@ (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) + (setq-local beginning-of-defun-function > 'css-beginning-of-defun) + (setq-local end-of-defun-function > 'css-end-of-defun) (when css-electric-keys (let ((fc (make-char-table > 'auto-fill-chars))) (set-char-table-parent fc auto-fill-chars) @@ -394,102 > +397,178 @@ ;; FIXME: We should also skip punctuation. (not (memq > (char-after) '(?\; ?\}))))))))))) -(defun css-indent-calculate-virtual () - > (if (or (save-excursion (skip-chars-backward " \t") (bolp)) - (if > (looking-at "\\s(") - (save-excursion - (forward-char 1) > (skip-chars-forward " \t") - (not (or (eolp) (looking-at > comment-start-skip)))))) - (current-column) - (css-indent-calculate))) > +(defun css-comment-line-p () + (interactive) + (cond ( (save-excursion + > (back-to-indentation) + (looking-at "/\\*")) + 1 ) + ( (nth 4 > (syntax-ppss)) + t))) + +(defun css-beginning-of-defun (&optional arg) + > (unless arg (setq arg 1)) + (when (progn + ;; What for? + (unless (zerop > (current-column)) + (end-of-line)) + (re-search-backward "^[^\n ].+{[ ]?$" > nil t arg)) + (while (save-excursion + (and (zerop (forward-line -1)) + > (string-match-p + "^[^}[:space:]/]" + (buffer-substring + > (line-beginning-position) + (line-end-position))))) + (forward-line -1)))) > + +(defun css-end-of-defun (&optional arg) + (interactive) + (unless arg > (setq arg 1)) + (ignore-errors + (when (cl-plusp (first (syntax-ppss))) + > (css-beginning-of-defun)) + (progn + (search-forward "{" nil t arg) + > (backward-char) + (forward-sexp) + (ignore-errors + (forward-char))) + t)) > + +;; To make writing derived modes easier. Ex. SASS also supports // type > comments +(defvar css-comment-line-p-function 'css-comment-line-p + "Should > return 1 if at the beginning of a comment, t if inside.") + +(defun > css--goto-prev-struct-line () + (while (and (zerop (forward-line -1)) + > (funcall css-comment-line-p-function)))) + +(defvar css-debug nil) + > +(defun css-indent-debug-msg (name) + (when css-debug + (message "%s" > name))) + +(defun css-visible-end-of-line () + (save-excursion + > (end-of-line) + (skip-syntax-backward + " " (line-beginning-position)) + > (point))) -(defcustom css-indent-offset 4 - "Basic size of one indentation > step." - :version "22.2" - :type 'integer - :group 'css) +(defun > css-indentation-end-pos () + (save-excursion + (back-to-indentation) + > (point))) -(defun css-indent-calculate () - (let ((ppss (syntax-ppss)) - > pos) - (with-syntax-table css-navigation-syntax-table - (save-excursion - > (cond - ;; Inside a string. - ((nth 3 ppss) 'noindent) - ;; Inside a > comment. - ((nth 4 ppss) - (setq pos (point)) - (forward-line -1) - > (skip-chars-forward " \t") - (if (>= (nth 8 ppss) (point)) - (progn - > (goto-char (nth 8 ppss)) - (if (eq (char-after pos) ?*) - (forward-char 1) > - (if (not (looking-at comment-start-skip)) - (error "Internal css-mode > error") - (goto-char (match-end 0)))) - (current-column)) - (if (and (eq > (char-after pos) ?*) (eq (char-after) ?*)) - (current-column) - ;; > 'noindent - (current-column) - ))) - ;; In normal code. - (t - (or - (when > (looking-at "\\s)") - (forward-char 1) - (backward-sexp 1) - > (css-indent-calculate-virtual)) - (when (looking-at comment-start-skip) - > (forward-comment (point-max)) - (css-indent-calculate)) - (when > (save-excursion (forward-comment (- (point-max))) - (setq pos (point)) - > (eq (char-syntax (preceding-char)) ?\()) - (goto-char (1- pos)) - (if (not > (looking-at "\\s([ \t]*")) - (error "Internal css-mode error") - (if (or > (memq (char-after (match-end 0)) '(?\n nil)) - (save-excursion (goto-char > (match-end 0)) - (looking-at comment-start-skip))) - (+ > (css-indent-calculate-virtual) css-indent-offset) - (progn (goto-char > (match-end 0)) (current-column))))) - (progn - (css-backward-sexp 1) - (if > (looking-at "\\s(") - (css-indent-calculate) - > (css-indent-calculate-virtual)))))))))) +(defun > css-current-character-indentation () + "Like (current-indentation), but > counts tabs as single characters." + (save-excursion + > (back-to-indentation) + (- (point) (line-beginning-position)))) + +(defvar > css-colon ":" + "A dervied mode for SASS or LESS might want to set this to > +\": \", to make nested pseudo-classes work.") +(defun css-indent-calculate > () + ;; If I go to the beginning of line, MC stops working + > (back-to-indentation) + (with-syntax-table css-navigation-syntax-table + > (let* (psl-indent + psl-last-char + psl-first-char + ( psl + > (save-excursion + (css--goto-prev-struct-line) + (setq psl-indent > (current-indentation)) + (setq psl-last-char (char-before > (css-visible-end-of-line))) + (setq psl-first-char (char-after > (css-indentation-end-pos))) + (buffer-substring + (line-beginning-position) > + (line-end-position)))) + ( psl-closing-brackets + (+ (cl-count ?} psl) + > (cl-count ?\) psl))) + ( psl-open-brackets (+ (cl-count ?{ psl) (cl-count > ?\( psl))) + ( psl-has-colon (cl-plusp (cl-count ?: psl))) + (ppss > (syntax-ppss)) + previous-comment-indent + previous-line-was-comment + pos) > + (cond ( ;; Inside a multiline comment + ( eq (funcall > css-comment-line-p-function) t) + (css-indent-debug-msg "MC") + > (save-excursion + (nth 4 ppss) + (setq pos (point)) + (forward-line -1) + > (skip-chars-forward " \t") + (if (>= (nth 8 ppss) (point)) + (progn + > (goto-char (nth 8 ppss)) + (if (eq (char-after pos) ?*) + (forward-char 1) > + (if (not (looking-at comment-start-skip)) + (error "Internal css-mode > error") + (goto-char (match-end 0)))) + (current-column)) + > (current-column)))) + ( ;; If "outside" indent to 0 + (zerop (nth 0 ppss)) > + (css-indent-debug-msg "ZERO") + 0) + ( ;; Not-first member of comma > ending lines + (and (not (cl-search css-colon psl)) + (equal psl-last-char > ?\, ) + (= psl-open-brackets psl-closing-brackets)) + (css-indent-debug-msg > "MCB") + psl-indent) + ( ;; Line after beginning of comma block + (and > (member psl-last-char '( ?: ?\, ) ) + (= psl-open-brackets > psl-closing-brackets)) + (css-indent-debug-msg "LABOC") + (+ psl-indent > css-indent-offset)) + ( ;; Default, based on nesting level + t + > (css-indent-debug-msg "LAST") + (let (( parent-indent + (save-excursion + > (backward-up-list) + (css-current-character-indentation))) + ( > block-ending-line + (member (char-after (css-indentation-end-pos)) + '( ?\} > ?\) ) ))) + (+ parent-indent + (* (+ (if block-ending-line -1 0) + 1) + > css-indent-offset)))) + )))) (defun css-indent-line () "Indent current line > according to CSS indentation rules." (interactive) - (let* ((savep (point)) > - (forward-sexp-function nil) - (indent (condition-case nil - > (save-excursion - (forward-line 0) - (skip-chars-forward " \t") - (if (>= > (point) savep) (setq savep nil)) - (css-indent-calculate)) - (error nil)))) > - (if (not (numberp indent)) 'noindent - (if savep - (save-excursion > (indent-line-to indent)) - (indent-line-to indent))))) + (save-excursion + > (indent-line-to (css-indent-calculate))) + (when (< (current-column) > (current-indentation)) + (back-to-indentation))) + +(defcustom > css-indent-offset 4 + "Basic size of one indentation step." + :version > "22.2" + :type 'integer + :group 'css) (defun css-current-defun-name () > "Return the name of the CSS section at point, or nil." (save-excursion (let > ((max (max (point-min) (- (point) 1600)))) ; approx 20 lines back (when > (search-backward "{" max t) - (skip-chars-backward " \t\r\n") - > (beginning-of-line) - (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") > - (match-string-no-properties 1)))))) + (skip-chars-backward " \t\r\n") + > (beginning-of-line) + (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") > + (match-string-no-properties 1)))))) (provide 'css-mode) ;;; css-mode.el > ends here \ No newline at end of file > > > On Tue, Jan 15, 2013 at 5:00 AM, Stefan Monnier wrote: > >> > I've written a new indenter for CSS, which fixes issues with mulitline >> > statements following a colon, and comments: >> >> Interesting, I don't use CSS myself very much, but I've toyed with an >> SMIE indenter for css-mode (see below). >> >> > body { >> > color: #333; >> > font: 15px "Helvetica Neue", >> > Helvetica, >> > Arial, >> > "Nimbus Sans L", >> > sans-serif; >> > font-weight: 300; >> > line-height: 1.625; >> > a { /* It also handles SCSS */ >> > font: 15px "Helvetica Neue", >> > Helvetica, >> > Arial, >> > "Nimbus Sans L", >> > sans-serif; >> > /* Some comment */ >> > } >> > /* Some comment at the end of a block */ >> > } >> >> I've tried it on the above test case and the SMIE code seems to handle >> it OK (tho differently), with the main exception being the >> comment-at-end-of-block, which is probably a general problem which >> should be fixed in smie.el (probably in smie-indent-comment). >> >> But I have basically never used/tested my code, so... >> >> > I wondered whether this functionality could be integrated into Emacs. >> > https://github.com/sabof/es-css-mode >> > https://github.com/sabof/es-lib (it takes some functions from it) >> >> If you could provide a patch against css-mode.el, that would be more >> convenient. Also it would be good to add a good set of test cases (in >> the form of a new file test/indent/css-mode.css). >> >> >> Stefan >> >> >> === modified file 'lisp/textmodes/css-mode.el' >> --- lisp/textmodes/css-mode.el 2013-01-02 16:13:04 +0000 >> +++ lisp/textmodes/css-mode.el 2013-01-15 04:53:03 +0000 >> @@ -263,6 +263,22 @@ >> (defvar css-font-lock-defaults >> '(css-font-lock-keywords nil t)) >> >> +(defcustom css-indent-offset 4 >> + "Basic size of one indentation step." >> + :version "22.2" >> + :type 'integer) >> + >> +(defconst css-smie-grammar >> + (smie-prec2->grammar >> + (smie-precs->prec2 '((assoc ";") (left ":") (assoc ","))))) >> + >> +(defun css-smie-rules (kind token) >> + (pcase (cons kind token) >> + (`(:elem . basic) css-indent-offset) >> + (`(:elem . arg) 0) >> + (`(:before . "{") (if (smie-rule-hanging-p) >> + (smie-rule-parent 0))))) >> + >> ;;;###autoload >> (define-derived-mode css-mode fundamental-mode "CSS" >> "Major mode to edit Cascading Style Sheets." >> @@ -271,11 +287,11 @@ >> (setq-local comment-start-skip "/\\*+[ \t]*") >> (setq-local comment-end "*/") >> (setq-local comment-end-skip "[ \t]*\\*+/") >> - (setq-local forward-sexp-function 'css-forward-sexp) >> (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) >> (when css-electric-keys >> (let ((fc (make-char-table 'auto-fill-chars))) >> (set-char-table-parent fc auto-fill-chars) >> @@ -355,131 +371,6 @@ >> ;; Don't use the default filling code. >> t))))))) >> >> -;;; Navigation and indentation. >> - >> -(defconst css-navigation-syntax-table >> - (let ((st (make-syntax-table css-mode-syntax-table))) >> - (map-char-table (lambda (c v) >> - ;; Turn punctuation (code = 1) into symbol (code = >> 1). >> - (if (eq (car-safe v) 1) >> - (set-char-table-range st c (cons 3 (cdr v))))) >> - st) >> - st)) >> - >> -(defun css-backward-sexp (n) >> - (let ((forward-sexp-function nil)) >> - (if (< n 0) (css-forward-sexp (- n)) >> - (while (> n 0) >> - (setq n (1- n)) >> - (forward-comment (- (point-max))) >> - (if (not (eq (char-before) ?\;)) >> - (backward-sexp 1) >> - (while (progn (backward-sexp 1) >> - (save-excursion >> - (forward-comment (- (point-max))) >> - ;; FIXME: We should also skip punctuation. >> - (not (or (bobp) (memq (char-before) '(?\; >> ?\{)))))))))))) >> - >> -(defun css-forward-sexp (n) >> - (let ((forward-sexp-function nil)) >> - (if (< n 0) (css-backward-sexp (- n)) >> - (while (> n 0) >> - (setq n (1- n)) >> - (forward-comment (point-max)) >> - (if (not (eq (char-after) ?\;)) >> - (forward-sexp 1) >> - (while (progn (forward-sexp 1) >> - (save-excursion >> - (forward-comment (point-max)) >> - ;; FIXME: We should also skip punctuation. >> - (not (memq (char-after) '(?\; ?\}))))))))))) >> - >> -(defun css-indent-calculate-virtual () >> - (if (or (save-excursion (skip-chars-backward " \t") (bolp)) >> - (if (looking-at "\\s(") >> - (save-excursion >> - (forward-char 1) (skip-chars-forward " \t") >> - (not (or (eolp) (looking-at comment-start-skip)))))) >> - (current-column) >> - (css-indent-calculate))) >> - >> -(defcustom css-indent-offset 4 >> - "Basic size of one indentation step." >> - :version "22.2" >> - :type 'integer >> - :group 'css) >> - >> -(defun css-indent-calculate () >> - (let ((ppss (syntax-ppss)) >> - pos) >> - (with-syntax-table css-navigation-syntax-table >> - (save-excursion >> - (cond >> - ;; Inside a string. >> - ((nth 3 ppss) 'noindent) >> - ;; Inside a comment. >> - ((nth 4 ppss) >> - (setq pos (point)) >> - (forward-line -1) >> - (skip-chars-forward " \t") >> - (if (>= (nth 8 ppss) (point)) >> - (progn >> - (goto-char (nth 8 ppss)) >> - (if (eq (char-after pos) ?*) >> - (forward-char 1) >> - (if (not (looking-at comment-start-skip)) >> - (error "Internal css-mode error") >> - (goto-char (match-end 0)))) >> - (current-column)) >> - (if (and (eq (char-after pos) ?*) (eq (char-after) ?*)) >> - (current-column) >> - ;; 'noindent >> - (current-column) >> - ))) >> - ;; In normal code. >> - (t >> - (or >> - (when (looking-at "\\s)") >> - (forward-char 1) >> - (backward-sexp 1) >> - (css-indent-calculate-virtual)) >> - (when (looking-at comment-start-skip) >> - (forward-comment (point-max)) >> - (css-indent-calculate)) >> - (when (save-excursion (forward-comment (- (point-max))) >> - (setq pos (point)) >> - (eq (char-syntax (preceding-char)) ?\()) >> - (goto-char (1- pos)) >> - (if (not (looking-at "\\s([ \t]*")) >> - (error "Internal css-mode error") >> - (if (or (memq (char-after (match-end 0)) '(?\n nil)) >> - (save-excursion (goto-char (match-end 0)) >> - (looking-at comment-start-skip))) >> - (+ (css-indent-calculate-virtual) css-indent-offset) >> - (progn (goto-char (match-end 0)) (current-column))))) >> - (progn >> - (css-backward-sexp 1) >> - (if (looking-at "\\s(") >> - (css-indent-calculate) >> - (css-indent-calculate-virtual)))))))))) >> - >> - >> -(defun css-indent-line () >> - "Indent current line according to CSS indentation rules." >> - (interactive) >> - (let* ((savep (point)) >> - (forward-sexp-function nil) >> - (indent (condition-case nil >> - (save-excursion >> - (forward-line 0) >> - (skip-chars-forward " \t") >> - (if (>= (point) savep) (setq savep nil)) >> - (css-indent-calculate)) >> - (error nil)))) >> - (if (not (numberp indent)) 'noindent >> - (if savep >> - (save-excursion (indent-line-to indent)) >> - (indent-line-to indent))))) >> >> (defun css-current-defun-name () >> "Return the name of the CSS section at point, or nil." >> >> > --20cf3074d594ecfdb804d370ebd7 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable
I've rewritten the indenter - this time I&#= 39;ve added a parser for statements within curly brackets, so there is a lo= t less hackery. I've also added some more tests cases.

Evgeni


New =A0= =A0 =A0 =A0css-mode.css
diff --git a/css-mode.css b/css-mode.css=
new file mode 100644
index 0000000..f101971
= --- /dev/null
+++ b/css-mode.css
@@ -0,0 +1,230 @@
+#top_menu .b= utton.active,
+#top_menu .button.active:active {
+ =A0 = =A0background-image: url('images/header_button_active.png');
<= div> + =A0 =A0cursor: default;
+ =A0 =A0color: #999;
+ =A0 = =A0text-shadow: 1px 1px 2px #111;
+ =A0 =A0/* Comment */
+}
+
+#top_menu .button.active,
+#top_menu = .button.active:active
+{
+ =A0 =A0/* Comment */
+ =A0 =A0background-imag= e: url('images/header_button_active.png');
+ =A0 =A0curso= r: default;
+ =A0 =A0color: #999;
+ =A0 =A0text-shadow:= 1px 1px 2px #111;
+ =A0 =A0/* Multiline
+ =A0 =A0 =A0 comment1 */
+ = =A0 =A0/* Multiline
+ =A0 =A0 * comment2 */
+}
+/* Multiline
+ =A0 comment1 */
+/* Multiline
+ * comment2 */
+#glass {
+ =A0 =A0z-index: 2;
+ =A0 =A0position: = absolute;
+ =A0 =A0top: -112px;
+ =A0 =A0left: 0;
=
+ =A0 =A0right: 0;
+ =A0 =A0margin: 0 0 0 0;
+ =A0= =A0margin:
+ =A0 =A0 =A0 =A00 0 0 0;
+ =A0 =A0text-shadow:
+ =A0 =A0 =A0 =A01px 1px #FDE31D,
+ =A0 =A0 =A0 =A0-1px -1px #B36300;
+ =A0 =A0height: 140px;=
+ =A0 =A0background-image: url('images/honey_blurred2.png= 9;);
+ =A0 =A0background-image: url(
+ =A0 =A0 =A0 =A0'images/honey_blurred2.png');
+ =A0= =A0background-image:
+ =A0 =A0 =A0 =A0#fff,
+ =A0 =A0 = =A0 =A0url('images/honey_blurred2.png'),
+ =A0 =A0 =A0 = =A0url('images/honey_blurred2.png');
+ =A0 =A0background-image:
+ =A0 =A0 =A0 =A0#fff,
= + =A0 =A0 =A0 =A0/* #fff, */
+ =A0 =A0 =A0 =A0url('images/hon= ey_blurred2.png');
+ =A0 =A0background-image: #fff,
+ =A0 =A0 =A0 =A0url('images/honey_blurred2.png');
+ =A0 =A0-webkit-mask-image:
+ =A0 =A0 =A0 =A0-webkit-gradie= nt(
+ =A0 =A0 =A0 =A0 =A0 =A0linear,
+ =A0 =A0 =A0 =A0 = =A0 =A0left top,
+ =A0 =A0 =A0 =A0 =A0 =A0/* left bottom, */
+ =A0 =A0 =A0 =A0 =A0 =A0left bottom,
+ =A0 =A0 =A0 =A0 =A0= =A0color-stop(
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01,
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0rgba(0,0,0,0.2)
+ =A0 =A0 =A0 =A0 =A0 =A0),
= + =A0 =A0 =A0 =A0 =A0 =A0color-stop(1,
+
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0rgba(0,0,0,0.2)
+ =A0 =A0 =A0 =A0 =A0 =A0),
+ =A0 =A0 =A0 =A0 =A0 =A0/* comment */
+ =A0 =A0 =A0 =A0 =A0 =A0c= olor-stop(0.7, rgba(0,0,0,.0)),
+ =A0 =A0 =A0 =A0 =A0 =A0color-st= op
+ =A0 =A0 =A0 =A0 =A0 =A0(
+ =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A00.7, rgba(0,0,0,.0)
+
+ =A0 =A0 =A0 =A0 =A0 =A0= )
+ =A0 =A0 =A0 =A0 =A0 =A0/* comment*/ );
+ =A0 =A0background= -repeat: repeat-x;
+ =A0 =A0background-position: center top;
+ =A0 =A0background-attachment: fixed; }
+
+#glas= s {
+ =A0 =A0margin:
+ =A0 =A0 =A0 =A0/* =A0 =A0 0 0 0 0; */
+ =A0 =A0 =A0 =A0/* = text-shadow: */
+ =A0 =A0 =A0 =A01px 1px #FDE31D,
+ =A0= =A0 =A0 =A0-1px -1px #B36300;
+ =A0 =A0height: 140px;
= + =A0 =A0background-image: url('images/honey_blurred2.png');
+ =A0 =A0/* background-image: url( */
+ =A0 =A0/* =A0 =A0 &#= 39;images/honey_blurred2.png'); */
+
+ =A0 =A0backg= round-image:
+ =A0 =A0 =A0 =A0#fff,
+ =A0 =A0 =A0 =A0ur= l('images/honey_blurred2.png'),
+ =A0 =A0 =A0 =A0url('images/honey_blurred2.png');
+= =A0 =A0background-image:
+ =A0 =A0 =A0 =A0#fff,
+ =A0 = =A0 =A0 =A0/* #fff, */
+ =A0 =A0 =A0 =A0/* =A0 =A0 url('image= s/honey_blurred2.png'); */
+ =A0 =A0 =A0 =A0/* background-image: #fff, */
+ =A0 =A0 =A0= =A0url('images/honey_blurred2.png');
+ =A0 =A0-webkit-ma= sk-image:
+ =A0 =A0 =A0 =A0-webkit-gradient(
+ =A0 =A0 = =A0 =A0 =A0 =A0linear,
+ =A0 =A0 =A0 =A0 =A0 =A0left top,
+ =A0 =A0 =A0 =A0 =A0 =A0/* left bottom, */
+ =A0 =A0 =A0 = =A0 =A0 =A0left bottom,
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop(
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* 1, */
+ =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0/* rgba(0,0,0,0.2) */
+ =A0 =A0 =A0 =A0 =A0 =A0),=
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop(1,
+ =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0/* com */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rgba(0,0,0= ,0.2)
+
+ =A0 =A0 =A0 =A0 =A0 =A0),
+ =A0 =A0= =A0 =A0 =A0 =A0/* comment */
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop= (0.7, rgba(0,0,0,.0)),
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop
+ =A0 =A0 =A0 =A0 =A0 = =A0(
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00.7, rgba(0,0,0= ,.0)
+ =A0 =A0 =A0 =A0 =A0 =A0)
+ =A0 =A0 =A0 =A0 =A0 = =A0/* comment*/);
+ =A0 =A0-webkit-mask-image:
+ =A0 =A0 =A0 =A0/* -webkit- */gradient(
+ =A0 =A0 =A0 =A0 =A0 = =A0linear,
+ =A0 =A0 =A0 =A0 =A0 =A0left /* top */,
+ = =A0 =A0 =A0 =A0 =A0 =A0/* left =A0*/bottom,
+ =A0 =A0 =A0 =A0 =A0= =A0left bottom,
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop(
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* 1, */
+ =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0/* rgba(0,0,0,0.2) */
+ =A0 =A0 =A0 =A0 =A0 =A0),<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop(1,
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0/* com */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rgb= a(0,0,0,0.2)
+
+ =A0 =A0 =A0 =A0 =A0 =A0),
+ =A0 =A0 =A0 =A0 = =A0 =A0/* comment */
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop(0.7/* , = rgba(0,0,0,.0) */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0, rgba(0,0,0,.= 0) ),
+ =A0 =A0 =A0 =A0 =A0 =A0color-stop
+ =A0 =A0 =A0 =A0 =A0 =A0(
+
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A00.7, rgba(0,0,0,.0)
+ =A0 =A0 =A0 =A0 =A0 =A0)
+= =A0 =A0 =A0 =A0 =A0 =A0/* comment*/);
+ =A0 =A0color: black,
+ =A0 =A0 =A0 =A0/* red */,
+ =A0 =A0 =A0 =A0blue;
+ =A0 =A0/* -webkit-mask-image: */
+ =A0 =A0background:
+ =A0 =A0 =A0 =A0-webkit-gradient(
+ =A0 =A0 =A0 =A0 =A0 = =A0/* com */
+ =A0 =A0);
+ =A0 =A0-webkit-mask-image: -= webkit-gradient(
+ =A0 =A0 =A0 =A0/* Forgivable? Better? */
+ =A0 =A0),
+ =A0 =A0 =A0 =A0-webkit-mask-image(
+= =A0 =A0 =A0 =A0 =A0 =A0/* Back on track */
+ =A0 =A0 =A0 =A0);
+ =A0 =A0background-attachment: fixed
+ =A0 =A0/* sdfsdf= */
+}
+
+p:nth-child(2) {
+ =A0 =A0margin:
+ =A0 =A0 =A0 =A00
+ =A0 =A0 =A0 =A00
+ =A0 =A0 =A0 =A00
+ =A0 = =A0 =A0 =A00;
+ =A0 =A0margin: 0 0
+ =A0 =A0 =A0 =A00
+ =A0 =A0 =A0 =A00;
+
+ =A0 =A0/* comment */
+
+ =A0 =A0text-shadow:1px 1px #FDE31D, /* no space */
=
+ =A0 =A0 =A0 =A0-1px -1px #B36300;
+}
+
+p:nth-child
+(
+ =A0 =A02
+)
+{
+ =A0 =A0height: 2px
+}
+
+#field_message {
+ =A0 =A0width: 1= 00%;
+ =A0 =A0display: block;
+ =A0 =A0-webkit-box-sizi= ng: border-box; /* Safari/Chrome, other WebKit */
+ =A0 =A0-moz-b= ox-sizing: border-box; =A0 =A0/* Firefox, other Gecko */
+ =A0 =A0box-sizing: border-box; =A0 =A0 =A0 =A0 /* Opera/IE 8+ */
+ =A0 =A0/* resize: vertical; */
+ =A0 =A0resize: none;
+ =A0 =A0height: 160px
+}
+
+
+some {
+
+}
+
+/* SASS */
+
+.so= me {
+ =A0 =A0/* resize: vertical; */
+ =A0 =A0resize: = none;
+
+ =A0 =A0height: 160px;
+ =A0 =A0.oth= er,
+ =A0 =A0.another,
+ =A0 =A0&:a,
+ =A0 =A0#id {
+ =A0 =A0 =A0 =A0/* resize: vertical; */
+ =A0 =A0 =A0 =A0= resize: none;
+
+ =A0 =A0 =A0 =A0height: 160px;
+ =A0 =A0}
+ =A0 =A0/* Comment */
+ =A0 =A0resize: none;
+ =A0 =A0height: 160px;
+ = =A0 =A0/* Comment */
+}
+
+
+/* Loc= al Variables: */
+/* css-debug: t */
+/* End: */
<= div>\ No newline at end of file
Modified =A0 css-mo= de.el
diff --git a/css-mode.el b/css-mode.el
index 1abe= 9a8..9f3a786 100644
--- a/css-mode.el
+++ b/css-mode.el=
@@ -31,12 +31,95 @@
=A0;; - completion
=A0;; - fix= font-lock errors with multi-line selectors
=A0
+;;; TO= DO:
+;; =A0extra empty line indentation
+
=A0= ;;; Code:
=A0
+(require 'cl-lib)
+
=A0(defgrou= p css nil
=A0 =A0"Cascading Style Sheets (CSS) editing mode.= "
=A0 =A0:group 'languages)
=A0
+;; = Debugging
+
+(defvar css-debug-overlays nil)
+
+(d= efun css-debug-overlay (beginning end color)
+ =A0(let ((overlay = (make-overlay beginning end)))
+ =A0 =A0(overlay-put overlay '= ;face `(:background ,color))
+ =A0 =A0(push overlay css-debug-overlays)))
+
+(d= efvar css-debug nil)
+
+(defun css-debug-msg (name)
+ =A0(when css-debug
+ =A0 =A0(message "%s" name= )))
+
+(defun css-debug-goto-root-declaration ()
+ =A0= (let* (( ppss (syntax-ppss))
+ =A0 =A0 =A0 =A0 ( depth (nth 0 =A0= ppss)))
+ =A0 =A0(when (nth 3 ppss)
+ =A0 =A0 =A0(goto-= char (nth 8 ppss)))
+ =A0 =A0(while (and (cl-plusp depth)
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(not (equal (char-after) ?\{ )))
+ =A0 =A0 =A0(up-list= -1)
+ =A0 =A0 =A0(cl-decf depth))))
+
+(defu= n css-debug-parser-current ()
+ =A0(interactive)
+ =A0(mapc 'delete-overlay css-debug-= overlays)
+ =A0(let* (( point
+ =A0 =A0 =A0 =A0 =A0 (sa= ve-excursion
+ =A0 =A0 =A0 =A0 =A0 =A0 (back-to-indentation)
+ =A0 =A0 =A0 =A0 =A0 =A0 (point)))
+ =A0 =A0 =A0 =A0 ( parsed
+ =A0 =A0 =A0 =A0 =A0 (save-excur= sion
+ =A0 =A0 =A0 =A0 =A0 =A0 (css-debug-goto-root-declaration)<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 (css-parse-curly)))
+ =A0 =A0= =A0 =A0 ( relevant
+ =A0 =A0 =A0 =A0 =A0 (css-pc-get-relevant pa= rsed point)))
+ =A0 =A0(css-debug-overlay
+ =A0 =A0 (car relevant)
+ =A0 =A0 (cadr relevant)
+ =A0 =A0 "DarkRed")
=
+ =A0 =A0nil))
+
+(defun css-debug-highight-parsed= (parsed)
+ =A0(mapc 'delete-overlay css-debug-overlays)
+ =A0(cl-mapc = (lambda (item)
+ =A0 =A0 =A0 =A0 =A0 =A0 (css-debug-overlay
=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0(car item)
+ =A0 =A0 =A0 =A0 = =A0 =A0 =A0(cadr item)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0(case (caddr = item)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0('nested-selector =A0"DarkGr= een")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0('comment =A0 =A0= =A0 =A0 =A0"SaddleBrown")
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0( t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"DarkRed"))))
+ =A0 =A0 =A0 =A0 =A0 parsed))
+
+(defun css-debug-pars= er-all ()
+ =A0(interactive)
+ =A0(let* (( parsed
=
+ =A0 =A0 =A0 =A0 =A0 (save-excursion
+ =A0 =A0 =A0 =A0 =A0 = =A0 (css-debug-goto-root-declaration)
+ =A0 =A0 =A0 =A0 =A0 =A0 (css-parse-curly))))
+ =A0 =A0(css= -debug-highight-parsed
+ =A0 =A0 parsed)
+ =A0 =A0nil))=
+
+(defun css-debug-parser-inside ()
+ =A0(i= nteractive)
+ =A0(let* (( parsed
+ =A0 =A0 =A0 =A0 =A0 (save-excursion
<= div>+ =A0 =A0 =A0 =A0 =A0 =A0 (css-debug-goto-root-declaration)
+= =A0 =A0 =A0 =A0 =A0 =A0 (css-parse-curly)))
+ =A0 =A0 =A0 =A0 ( = inside
+ =A0 =A0 =A0 =A0 =A0 (css-pc-inside-statement
+ =A0 =A0 =A0 =A0 =A0 =A0parsed (point))))
+ =A0 =A0(message= "P %s" inside)))
+
+;; EOF Debugging
=A0
=A0(defun css-extract-keyword-list (res)
=A0 =A0= (with-temp-buffer
@@ -92,7 +175,6 @@
=A0 =A0 =A0 =A0 (t nil)))
=A0 = =A0 =A0elems))
=A0
-
=A0(defun css-extract-pr= ops-and-vals ()
=A0 =A0(with-temp-buffer
=A0 =A0 =A0(ur= l-insert-file-contents "http://www.w3.org/TR/CSS21/propidx.html")
@@ -122,7 +204,7 @@
=A0
=A0(defconst css-pseudo-id= s
=A0 =A0'("active" "after" "before&= quot; "first" "first-child" "first-letter" &q= uot;first-line"
- =A0 =A0"focus" "hover" "lang" "le= ft" "link" "right" "visited")
= + =A0 =A0"focus" "hover" "lang" "last-ch= ild" "left" "link" "right" "visited= ")
=A0 =A0"Identifiers for pseudo-elements and pseudo-classes."= )
=A0
=A0(defconst css-at-ids
@@ -264,8 +346,= 8 @@
=A0 =A0'(css-font-lock-keywords nil t))
=A0
=A0;;;###autoload
-(define-derived-mode css-mode fundamental-mode "CSS"
<= div>- =A0"Major mode to edit Cascading Style Sheets."
+= (define-derived-mode css-mode fundamental-mode
+ =A0"CSS&quo= t; "Major mode to edit Cascading Style Sheets."
=A0 =A0(setq-local font-lock-defaults css-font-lock-defaults)
=A0 =A0(setq-local comment-start "/*")
=A0 =A0(setq-l= ocal comment-start-skip "/\\*+[ \t]*")
@@ -276,6 +358,8= @@
=A0 =A0(setq-local indent-line-function 'css-indent-line)
=A0 =A0(setq-local fill-paragraph-function 'css-fill-paragraph)
=
=A0 =A0(setq-local add-log-current-defun-function #'css-current-de= fun-name)
+ =A0(setq-local beginning-of-defun-function 'css-beginning-of-def= un)
+ =A0(setq-local end-of-defun-function 'css-end-of-defun)=
=A0 =A0(when css-electric-keys
=A0 =A0 =A0(let ((fc (m= ake-char-table 'auto-fill-chars)))
=A0 =A0 =A0 =A0(set-char-table-parent fc auto-fill-chars)
@@= -394,102 +478,188 @@
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0;; FIXME: We should also skip punctuation.
=A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(not (memq (char-after) '= ;(?\; ?\})))))))))))
=A0
-(defun css-indent-calculate-virtual ()
- =A0(= if (or (save-excursion (skip-chars-backward " \t") (bolp))
<= div>- =A0 =A0 =A0 =A0 =A0(if (looking-at "\\s(")
- =A0 = =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-char 1) (skip-chars-forward = " \t")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(not (or (eolp)= (looking-at comment-start-skip))))))
- =A0 =A0 =A0(current-colum= n)
- =A0 =A0(css-indent-calculate)))
-
-(defcustom css-indent-offset 4
- =A0"Basic= size of one indentation step."
- =A0:version "22.2&quo= t;
- =A0:type 'integer
- =A0:group 'css)
<= div> +(defun css-beginning-of-defun (&optional arg)
+ =A0(unless a= rg (setq arg 1))
+ =A0(when (progn
+ =A0 =A0 =A0 =A0 = =A0;; What for?
+ =A0 =A0 =A0 =A0 =A0(unless (zerop (current-colu= mn))
+ =A0 =A0 =A0 =A0 =A0 =A0(end-of-line))
+ =A0 =A0 =A0 =A0 =A0(re-search-backward "^[^\n ].+{[ ]?$" nil t arg))
+ = =A0 =A0(while (save-excursion
+ =A0 =A0 =A0 =A0 =A0 =A0 (and (zer= op (forward-line -1))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(string-match-p
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "^[^}[:space:]/]"
+ =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (buffer-substring
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0(line-beginning-position)
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(line-end-position)))))
+ =A0 =A0 =A0(forward-line -1))))
+
+(defun css-en= d-of-defun (&optional arg)
+ =A0(interactive)
+ =A0= (unless arg (setq arg 1))
+ =A0(ignore-errors
+ =A0 =A0= (when (cl-plusp (car (syntax-ppss)))
+ =A0 =A0 =A0(css-beginning-of-defun))
+ =A0 =A0(progn
=
+ =A0 =A0 =A0(search-forward "{" nil t arg)
+ =A0 = =A0 =A0(backward-char)
+ =A0 =A0 =A0(forward-sexp)
+ = =A0 =A0 =A0(ignore-errors
+ =A0 =A0 =A0 =A0(forward-char)))
+ =A0 =A0t))
+
<= div>+(defun css-go-up ()
+ =A0(let* (( ppss (syntax-ppss)))
=
+ =A0 =A0(when (or (nth 3 ppss) (nth 4 ppss))
+ =A0 =A0 =A0(= goto-char (nth 8 ppss)))
+ =A0 =A0(when (cl-plusp (nth 0 ppss))
+ =A0 =A0 =A0(up-list= -1))))
+
+(defmacro css-while-point-moving (&rest = rest)
+ =A0(let ((old-point (cl-gensym)))
+ =A0 =A0`(le= t (,old-point)
+ =A0 =A0 =A0 (while (not (equal (point) ,old-point))
+ =A0 = =A0 =A0 =A0 (setq ,old-point (point))
+ =A0 =A0 =A0 =A0 ,@rest)))= )
+
+(defun css-parse-curly ()
+ =A0(let (( s= tart (point))
+ =A0 =A0 =A0 =A0( indentation (current-indentation))
+ =A0 =A0 = =A0 =A0( end (save-excursion
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(fo= rward-sexp)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(point)))
= + =A0 =A0 =A0 =A0point result)
+ =A0 =A0(forward-char)
+ =A0 =A0(cl-loop named main-loop
+ =A0 =A0 =A0 =A0 =A0 =A0 = do
+ =A0 =A0 =A0 =A0 =A0 =A0 (skip-chars-forward "\n\t "= ; end)
+ =A0 =A0 =A0 =A0 =A0 =A0 (when (>=3D (point) (1- end))=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 (cl-return-from main-loop))
+ =A0 =A0 =A0 =A0 =A0 =A0 (setq point (point))
+ =A0 =A0 =A0= =A0 =A0 =A0 (if (forward-comment 1)
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 (push (list point (point) 'comment) result)
+ =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 (progn
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 (cl-loop (unless (re-search-forward ";\\|{\\|}" end t)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(cl-retur= n-from main-loop))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(unless (nth 4 (syntax-ppss))
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(cl-return)))
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 (cond ( (equal (char-before) ?\{ )
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (backward-char)<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (forward-se= xp)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (push (= list point (point) 'nested-selector) result))
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ( (equal (char-before) ?\} )
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (backward-char)<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-while-= point-moving
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0(skip-chars-backward "\n\t " start)
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-comment -1))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (push (list poin= t (point) 'statement) result))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 ( t (push (list point (point) 'statement) result)))= )))
+ =A0 =A0(nreverse result)))
+
+(defun css-pc-get-relevant (parsed point)
+ =A0(car = (reverse (cl-remove-if (apply-partially '< point)
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0parsed :key 'car= ))))
+
+(defun css-pc-inside-statement (parsed point)
+ =A0(cl-some (lambda (item)
+ =A0 =A0 =A0 =A0 =A0 (and (<= ;=3D (car item) point)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(<=3D = point (cadr item))))
+ =A0 =A0 =A0 =A0 parsed))
=A0
=A0(defun css-indent-calculate ()
- =A0(let ((ppss (syntax-ppss))
- =A0 =A0 =A0 =A0pos)
<= div>- =A0 =A0(with-syntax-table css-navigation-syntax-table
- =A0= =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0(cond
- =A0 = =A0 =A0 =A0 ;; Inside a string.
- =A0 =A0 =A0 =A0 ((nth 3 ppss) 'noindent)
- =A0 =A0 =A0= =A0 ;; Inside a comment.
- =A0 =A0 =A0 =A0 ((nth 4 ppss)
- =A0 =A0 =A0 =A0 =A0(setq pos (point))
- =A0 =A0 =A0 =A0 =A0(= forward-line -1)
- =A0 =A0 =A0 =A0 =A0(skip-chars-forward " = \t")
- =A0 =A0 =A0 =A0 =A0(if (>=3D (nth 8 ppss) (point))
- = =A0 =A0 =A0 =A0 =A0 =A0 =A0(progn
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0(goto-char (nth 8 ppss))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if = (eq (char-after pos) ?*)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0(forward-char 1)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (not (looking-at comment-star= t-skip))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(error &quo= t;Internal css-mode error")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(goto-char (match-end 0))))
- =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0(current-column))
- =A0 =A0 =A0 =A0 =A0 =A0(if (and (eq (char-after pos) ?*) (eq (char-a= fter) ?*))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(current-column)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0;; 'noindent
- =A0 =A0 =A0= =A0 =A0 =A0 =A0(current-column)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0)))=
- =A0 =A0 =A0 =A0 ;; In normal code.
- =A0 =A0 =A0 =A0 (t
- =A0 =A0 =A0 =A0 =A0(or
- =A0 =A0 =A0 =A0 =A0 (when (loo= king-at "\\s)")
- =A0 =A0 =A0 =A0 =A0 =A0 (forward-char= 1)
- =A0 =A0 =A0 =A0 =A0 =A0 (backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate-virtual))
- = =A0 =A0 =A0 =A0 =A0 (when (looking-at comment-start-skip)
- =A0 = =A0 =A0 =A0 =A0 =A0 (forward-comment (point-max))
- =A0 =A0 =A0 = =A0 =A0 =A0 (css-indent-calculate))
- =A0 =A0 =A0 =A0 =A0 (when (save-excursion (forward-comment (- (point-max)= ))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 (setq pos (point))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 (eq (char-syntax (preceding-char)) ?\())
- =A0 =A0 =A0 =A0 =A0 =A0 (goto-char (1- pos))
- =A0 =A0 =A0= =A0 =A0 =A0 (if (not (looking-at "\\s([ \t]*"))
- =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 (error "Internal css-mode error")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 (if (or (memq (char-after (match-end 0= )) '(?\n nil))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (save-excursion (goto-ch= ar (match-end 0))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (looking-at comment-start-skip)))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (+ (css-indent-calculate-virtual) cs= s-indent-offset)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (progn (goto-char (match-end 0)) (cu= rrent-column)))))
- =A0 =A0 =A0 =A0 =A0 (progn
- =A0 = =A0 =A0 =A0 =A0 =A0 (css-backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 = =A0 (if (looking-at "\\s(")
- =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 (css-indent-calculate)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate-virtual))))))))))<= /div>
-
+ =A0(save-match-data
+ =A0 =A0(condition-c= ase error
+ =A0 =A0 =A0 =A0(with-syntax-table css-navigation-synt= ax-table
+ =A0 =A0 =A0 =A0 =A0(back-to-indentation)
+ =A0 =A0 =A0 =A0 =A0(let* ((point (point))
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 (ppss (syntax-ppss))
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 ( css-curly-parsed
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 (save-excursion
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (cs= s-go-up)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (when (equal (char-after) ?{= )
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-parse-curly= ))))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 css-parsed-relevant
<= div>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (block-ending-line
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(member (char-after
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (save-excursion<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (back-t= o-indentation)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 (point)))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0'( ?\} ?\) ) )))
+ =A0 =A0 =A0 =A0 =A0 =A0(cond ( (nth 4 ppss)
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0;; Inside a multiline comment
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(css-debug-msg "MC")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-line -1)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(skip-chars-forward "= ; \t")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (>= ;=3D (nth 8 ppss) (point))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0(progn
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0(goto-char (nth 8 ppss))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (eq (char= -after point) ?*)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0(forward-char 1)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (not (looking-at comment-start-skip= ))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 (error "Internal css-mode error")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = (goto-char (match-end 0))))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 (current-column))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 (current-column))))
+ =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 ( ;; If "outside" indent to 0
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(zerop (nth 0 ppss))
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(css-debug-msg "ZERO")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00)
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 ( ;; inside curly brackets
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(and css-curly-parsed
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (not block-ending-line)<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (setq css-parsed-re= levant
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = (css-pc-get-relevant
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0css-curly-parsed point))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (not (eq (nth 2 css-pars= ed-relevant)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0'nested-selector)))
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(css-debug-msg "C")
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0(+ css-indent-offset
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (save-excursion
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-go-up)
+ =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (current-indentation))
+ =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (if (and (not (equal (line-number-at-p= os
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 (car css-parsed-relevant))
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(line-number= -at-pos)))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0(css-pc-inside-statement
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 css-curl= y-parsed point))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 css-indent-offset 0)))
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ( ;;= Inside parentheses, closing brackets
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0t
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(css-debug-msg "P")
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(+ (save-excursion
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-go-up)
+ =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (current-indentation))
+ =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (if block-ending-line
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0 css-indent-offset)= )))))
+ =A0 =A0 =A0(error ;; My best error-less guess
+= =A0 =A0 =A0 (css-debug-msg "Err")
+ =A0 =A0 =A0 (* (ca= r (syntax-ppss))
+ =A0 =A0 =A0 =A0 =A0css-indent-offset)))))
=A0
=A0(defun css-indent-line ()
=A0 =A0"Inde= nt current line according to CSS indentation rules."
=A0 =A0= (interactive)
- =A0(let* ((savep (point))
- =A0 =A0 =A0= =A0 (forward-sexp-function nil)
- (indent (conditio= n-case nil
- = =A0 =A0 (save-excursion
- =A0 =A0 =A0 (forward-line 0)
- =A0 =A0 =A0 (ski= p-chars-forward " \t")
- =A0 =A0 =A0 (if (>=3D (point) savep) (setq savep n= il))
- =A0 = =A0 =A0 (css-indent-calculate))
- =A0 (error nil))= ))
- =A0 =A0(if (not (numberp indent)) 'noindent
- = =A0 =A0 =A0(if savep
- =A0 =A0 =A0 =A0 =A0(save-excursion (indent= -line-to indent))
- =A0 =A0 =A0 =A0(indent-line-to indent)))))
+ =A0(save-excu= rsion
+ =A0 =A0(indent-line-to (css-indent-calculate)))
+ =A0(when (< (current-column) (current-indentation))
+ =A0 = =A0(back-to-indentation)))
+
+(defcustom css-indent-offset 4
+ =A0"Basic= size of one indentation step."
+ =A0:version "22.2&quo= t;
+ =A0:type 'integer
+ =A0:group 'css)
<= div> =A0
=A0(defun css-current-defun-name ()
=A0 =A0"Re= turn the name of the CSS section at point, or nil."
=A0 =A0(= save-excursion
=A0 =A0 =A0(let ((max (max (point-min) (- (point) = 1600)))) =A0; approx 20 lines back
=A0 =A0 =A0 =A0(when (search-backward "{" max t)
-= (skip-chars-backward &qu= ot; \t\r\n")
- (beginning-of-line)
- (if (looking-at &q= uot;^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)")
- =A0 =A0(match-string-no-properties 1))))))=
+ =A0 =A0 =A0 =A0(skip-chars-backward " \t\r\n")
+ =A0 = =A0 =A0 =A0(beginning-of-line)
+ =A0 =A0 =A0 =A0(if (looking-at &= quot;^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)")
+ =A0 =A0 =A0 =A0 = =A0 =A0(match-string-no-properties 1))))))
=A0
=A0(provide 'css-mode)
=A0;;; css-mode.el = ends here
\ No newline at end of file

<= div class=3D"gmail_extra">

On Tue, Jan 15= , 2013 at 7:32 PM, E Sabof <esabof@gmail.com> wrote:
Here is a patch, including a more advanced test case. = In the end there is a "failing" section - although I haven't = seen anyone breaking at spaces.

There is a varaiable called css-colo= n - the default is ":". If set to ": ", it will also in= dent statements like this in SASS (a CSS pre-processor)

a { &:hover,=20 &:active { background: green; } }

However, it will no longer corr= ectly indent statements like this:

background:#000, /* = no space */ url('image.png');

If I manage= to fix the "breaking at spaces" case, I'll send another patc= h.

Evgeni

=A0New css-mode.css diff --git a/css-mode.css b/css-mode.css new file mode 100644 index 0000000..40a732f --- /dev/null +++ b/css-mode.css @@ -0,0 +1,86 @@ +#top_menu .button.active, +#top_menu .button.active:active { + background-image: url('images/header_button_active.png'); + cursor: default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Comment */ +} +#top_menu .button.active, +#top_menu .button.active:active +{ + /* Comment */ + background-image: url('images/header_button_active.png'); + cursor: default; + color: #999; + text-shadow: 1px 1px 2px #111; + /* Multiline + comment1 */ + /* Multiline + * comment2 */ +} +/* Multiline + comment1 */ +/* Multiline + * comment2 */ +#glass { + z-index: 2; + position: absolute; + top: -112px; + left: 0; + right: 0; + margin: 0 0 0 0; + margin: + 0 0 0 0; + text-shadow: + 1px 1px #FDE31D, + -1px -1px #B36300; + height: 140px; + background-image: url('images/honey_blurred2.png'); + background-image: url( + 'images/honey_blurred2.png'); + background-image: + #fff, + url('images/honey_blurred2.png'), + url('images/honey_blurred2.png'); + background-image: + #fff, + /* #fff, */ + url('images/honey_blurred2.png'); + background-image: #fff, + url('images/honey_blurred2.png'); + -webkit-mask-image: + -webkit-gradient( + linear, + left top, + /* left bottom, */ + left bottom, + color-stop( + 1, + rgba(0,0,0,0.2) + ), + color-stop(1, + rgba(0,0,0,0.2) + ), + /* comment */ + color-stop(0.7, rgba(0,0,0,.0)), + color-stop + ( + 0.7, rgba(0,0,0,.0) + ) + /* comment*/ ); + background-repeat: repeat-x; + background-position: center top; + background-attachment: fixed; } + +#failing { + margin: 0 + 0 + 0 + 0; + margin: + 0 + 0 + 0 + 0; +} \ No newline at end of file Modified css-mode.el diff --git a/css-mode.el b/css-mode.el index 1abe9a8..44682e1 100644 --- a/css-mode.el +++ b/css-mode.el @@ -33,6 +33,8 @@ =20 ;;; Code: =20 +(require 'cl-lib) + (defgroup css nil "Cascading Style Sheets (CSS) editing mode." :group 'languages) @@ -92,7 +94,6 @@ (t nil))) elems)) =20 - (defun css-extract-props-and-vals () (with-temp-buffer (url-insert-file-contents "http://www.w3.org/TR/CSS21/propidx.html= ") @@ -122,7 +123,7 @@ =20 (defconst css-pseudo-ids '("active" "after" "before" "firs= t" "first-child" "first-letter" "first-line&q= uot; - "focus" "hover" "lang" "left" = "link" "right" "visited") + "focus" "hover" "lang" "last-child&= quot; "left" "link" "right" "visited&quo= t;) "Identifiers for pseudo-elements and pseudo-classes.") =20 (defconst css-at-ids @@ -264,8 +265,8 @@ '(css-font-lock-keywords nil t)) =20 ;;;###autoload -(define-derived-mode css-mode fundamental-mode "CSS" - "Major mode to edit Cascading Style Sheets." +(define-derived-mode css-mode fundamental-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]*") @@ -276,6 +277,8 @@ (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) + (setq-local beginning-of-defun-function 'css-beginning-of-defun) + (setq-local end-of-defun-function 'css-end-of-defun) (when css-electric-keys (let ((fc (make-char-table 'auto-fill-chars))) (set-char-table-parent fc auto-fill-chars) @@ -394,102 +397,178 @@ ;; FIXME: We should also skip punctuation. (not (memq (char-after) '(?\; ?\}))))))))))) =20 -(defun css-indent-calculate-virtual () - (if (or (save-excursion (skip-chars-backward " \t") (bolp)) - (if (looking-at "\\s(") - (save-excursion - (forward-char 1) (skip-chars-forward " \t") - (not (or (eolp) (looking-at comment-start-skip)))))) - (current-column) - (css-indent-calculate))) +(defun css-comment-line-p () + (interactive) + (cond ( (save-excursion + (back-to-indentation) + (looking-at "/\\*")) + 1 ) + ( (nth 4 (syntax-ppss)) + t))) + +(defun css-beginning-of-defun (&optional arg) + (unless arg (setq arg 1)) + (when (progn + ;; What for? + (unless (zerop (current-column)) + (end-of-line)) + (re-search-backward "^[^\n ].+{[ ]?$" nil t arg)) + (while (save-excursion + (and (zerop (forward-line -1)) + (string-match-p + "^[^}[:space:]/]" + (buffer-substring + (line-beginning-position) + (line-end-position))))) + (forward-line -1)))) + +(defun css-end-of-defun (&optional arg) + (interactive) + (unless arg (setq arg 1)) + (ignore-errors + (when (cl-plusp (first (syntax-ppss))) + (css-beginning-of-defun)) + (progn + (search-forward "{" nil t arg) + (backward-char) + (forward-sexp) + (ignore-errors + (forward-char))) + t)) + +;; To make writing derived modes easier. Ex. SASS also supports // type co= mments +(defvar css-comment-line-p-function 'css-comment-line-p + "Should return 1 if at the beginning of a comment, t if inside.&quo= t;) + +(defun css--goto-prev-struct-line () + (while (and (zerop (forward-line -1)) + (funcall css-comment-line-p-function)))) + +(defvar css-debug nil) + +(defun css-indent-debug-msg (name) + (when css-debug + (message "%s" name))) + +(defun css-visible-end-of-line () + (save-excursion + (end-of-line) + (skip-syntax-backward + " " (line-beginning-position)) + (point))) =20 -(defcustom css-indent-offset 4 - "Basic size of one indentation step." - :version "22.2" - :type 'integer - :group 'css) +(defun css-indentation-end-pos () + (save-excursion + (back-to-indentation) + (point))) =20 -(defun css-indent-calculate () - (let ((ppss (syntax-ppss)) - pos) - (with-syntax-table css-navigation-syntax-table - (save-excursion - (cond - ;; Inside a string. - ((nth 3 ppss) 'noindent) - ;; Inside a comment. - ((nth 4 ppss) - (setq pos (point)) - (forward-line -1) - (skip-chars-forward " \t") - (if (>=3D (nth 8 ppss) (point)) - (progn - (goto-char (nth 8 ppss)) - (if (eq (char-after pos) ?*) - (forward-char 1) - (if (not (looking-at comment-start-skip)) - (error "Internal css-mode error") - (goto-char (match-end 0)))) - (current-column)) - (if (and (eq (char-after pos) ?*) (eq (char-after) ?*)) - (current-column) - ;; 'noindent - (current-column) - ))) - ;; In normal code. - (t - (or - (when (looking-at "\\s)") - (forward-char 1) - (backward-sexp 1) - (css-indent-calculate-virtual)) - (when (looking-at comment-start-skip) - (forward-comment (point-max)) - (css-indent-calculate)) - (when (save-excursion (forward-comment (- (point-max))) - (setq pos (point)) - (eq (char-syntax (preceding-char)) ?\()) - (goto-char (1- pos)) - (if (not (looking-at "\\s([ \t]*")) - (error "Internal css-mode error") - (if (or (memq (char-after (match-end 0)) '(?\n nil)) - (save-excursion (goto-char (match-end 0)) - (looking-at comment-start-skip))) - (+ (css-indent-calculate-virtual) css-indent-offset) - (progn (goto-char (match-end 0)) (current-column))))) - (progn - (css-backward-sexp 1) - (if (looking-at "\\s(") - (css-indent-calculate) - (css-indent-calculate-virtual)))))))))) +(defun css-current-character-indentation () + "Like (current-indentation), but counts tabs as single characters.&= quot; + (save-excursion + (back-to-indentation) + (- (point) (line-beginning-position)))) + +(defvar css-colon ":" + "A dervied mode for SASS or LESS might want to set this to +\": \", to make nested pseudo-classes work.") =20 +(defun css-indent-calculate () + ;; If I go to the beginning of line, MC stops working + (back-to-indentation) + (with-syntax-table css-navigation-syntax-table + (let* (psl-indent + psl-last-char + psl-first-char + ( psl + (save-excursion + (css--goto-prev-struct-line) + (setq psl-indent (current-indentation)) + (setq psl-last-char (char-before (css-visible-end-of-line))= ) + (setq psl-first-char (char-after (css-indentation-end-pos))= ) + (buffer-substring + (line-beginning-position) + (line-end-position)))) + ( psl-closing-brackets + (+ (cl-count ?} psl) + (cl-count ?\) psl))) + ( psl-open-brackets (+ (cl-count ?{ psl) (cl-count ?\( psl))) + ( psl-has-colon (cl-plusp (cl-count ?: psl))) + (ppss (syntax-ppss)) + previous-comment-indent + previous-line-was-comment + pos) + (cond ( ;; Inside a multiline comment + ( eq (funcall css-comment-line-p-function) t) + (css-indent-debug-msg "MC") + (save-excursion + (nth 4 ppss) + (setq pos (point)) + (forward-line -1) + (skip-chars-forward " \t") + (if (>=3D (nth 8 ppss) (point)) + (progn + (goto-char (nth 8 ppss)) + (if (eq (char-after pos) ?*) + (forward-char 1) + (if (not (looking-at comment-start-skip)) + (error "Internal css-mode error") + (goto-char (match-end 0)))) + (current-column)) + (current-column)))) + ( ;; If "outside" indent to 0 + (zerop (nth 0 ppss)) + (css-indent-debug-msg "ZERO") + 0) + ( ;; Not-first member of comma ending lines + (and (not (cl-search css-colon psl)) + (equal psl-last-char ?\, ) + (=3D psl-open-brackets psl-closing-brackets)) + (css-indent-debug-msg "MCB") + psl-indent) + ( ;; Line after beginning of comma block + (and (member psl-last-char '( ?: ?\, ) ) + (=3D psl-open-brackets psl-closing-brackets)) + (css-indent-debug-msg "LABOC") + (+ psl-indent css-indent-offset)) + ( ;; Default, based on nesting level + t + (css-indent-debug-msg "LAST") + (let (( parent-indent + (save-excursion + (backward-up-list) + (css-current-character-indentation))) + ( block-ending-line + (member (char-after (css-indentation-end-pos)) + '( ?\} ?\) ) ))) + (+ parent-indent + (* (+ (if block-ending-line -1 0) + 1) + css-indent-offset)))) + )))) =20 (defun css-indent-line () "Indent current line according to CSS indentation rules." (interactive) - (let* ((savep (point)) - (forward-sexp-function nil) - (indent (condition-case nil - (save-excursion - (forward-line 0) - (skip-chars-forward " \t") - (if (>=3D (point) savep) (setq savep nil)) - (css-indent-calculate)) - (error nil)))) - (if (not (numberp indent)) 'noindent - (if savep - (save-excursion (indent-line-to indent)) - (indent-line-to indent))))) + (save-excursion + (indent-line-to (css-indent-calculate))) + (when (< (current-column) (current-indentation)) + (back-to-indentation))) + +(defcustom css-indent-offset 4 + "Basic size of one indentation step." + :version "22.2" + :type 'integer + :group 'css) =20 (defun css-current-defun-name () "Return the name of the CSS section at point, or nil." (save-excursion (let ((max (max (point-min) (- (point) 1600)))) ; approx 20 lines bac= k (when (search-backward "{" max t) - (skip-chars-backward " \t\r\n") - (beginning-of-line) - (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") - (match-string-no-properties 1)))))) + (skip-chars-backward " \t\r\n") + (beginning-of-line) + (if (looking-at "^[ \t]*\\([^{\r\n]*[^ {\t\r\n]\\)") + (match-string-no-properties 1)))))) =20 (provide 'css-mode) ;;; css-mode.el ends here \ No newline at end of file


On Tue, Jan 15, 2013 at 5:00 AM, Ste= fan Monnier <monnier@iro.umontreal.ca> wrote:
> I've written a new indenter for CSS= , which fixes issues with mulitline
> statements following a colon, and comments:

Interesting, I don't use CSS myself very much, but I've toyed with = an
SMIE indenter for css-mode (see below).

> body {
> =A0 =A0 color: #333;
> =A0 =A0 font: 15px "Helvetica Neue",
> =A0 =A0 =A0 =A0 Helvetica,
> =A0 =A0 =A0 =A0 Arial,
> =A0 =A0 =A0 =A0 "Nimbus Sans L",
> =A0 =A0 =A0 =A0 sans-serif;
> =A0 =A0 font-weight: 300;
> =A0 =A0 line-height: 1.625;
> =A0 =A0 a { /* It also handles SCSS */
> =A0 =A0 =A0 =A0 font: 15px "Helvetica Neue",
> =A0 =A0 =A0 =A0 =A0 =A0 Helvetica,
> =A0 =A0 =A0 =A0 =A0 =A0 Arial,
> =A0 =A0 =A0 =A0 =A0 =A0 "Nimbus Sans L",
> =A0 =A0 =A0 =A0 =A0 =A0 sans-serif;
> =A0 =A0 =A0 =A0 /* Some comment */
> =A0 =A0 }
> =A0 =A0 /* Some comment at the end of a block */
> }

I've tried it on the above test case and the SMIE code seems to handle<= br> it OK (tho differently), with the main exception being the
comment-at-end-of-block, which is probably a general problem which
should be fixed in smie.el (probably in smie-indent-comment).

But I have basically never used/tested my code, so...

> I wondered whether this functionality could be integrated into Emacs.<= br> > htt= ps://github.com/sabof/es-css-mode
> https://= github.com/sabof/es-lib (it takes some functions from it)

If you could provide a patch against css-mode.el, that would be more
convenient. =A0Also it would be good to add a good set of test cases (in the form of a new file test/indent/css-mode.css).


=A0 =A0 =A0 =A0 Stefan


=3D=3D=3D modified file 'lisp/textmodes/css-mode.el'
--- lisp/textmodes/css-mode.el =A02013-01-02 16:13:04 +0000
+++ lisp/textmodes/css-mode.el =A02013-01-15 04:53:03 +0000
@@ -263,6 +263,22 @@
=A0(defvar css-font-lock-defaults
=A0 =A0'(css-font-lock-keywords nil t))

+(defcustom css-indent-offset 4
+ =A0"Basic size of one indentation step."
+ =A0:version "22.2"
+ =A0:type 'integer)
+
+(defconst css-smie-grammar
+ =A0(smie-prec2->grammar
+ =A0 (smie-precs->prec2 '((assoc ";") (left ":"= ) (assoc ",")))))
+
+(defun css-smie-rules (kind token)
+ =A0(pcase (cons kind token)
+ =A0 =A0(`(:elem . basic) css-indent-offset)
+ =A0 =A0(`(:elem . arg) 0)
+ =A0 =A0(`(:before . "{") (if (smie-rule-hanging-p)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (smie-rule-parent 0)))))<= br> +
=A0;;;###autoload
=A0(define-derived-mode css-mode fundamental-mode "CSS"
=A0 =A0"Major mode to edit Cascading Style Sheets."
@@ -271,11 +287,11 @@
=A0 =A0(setq-local comment-start-skip "/\\*+[ \t]*")
=A0 =A0(setq-local comment-end "*/")
=A0 =A0(setq-local comment-end-skip "[ \t]*\\*+/")
- =A0(setq-local forward-sexp-function 'css-forward-sexp)
=A0 =A0(setq-local parse-sexp-ignore-comments t)
=A0 =A0(setq-local indent-line-function 'css-indent-line)
=A0 =A0(setq-local fill-paragraph-function 'css-fill-paragraph)
=A0 =A0(setq-local add-log-current-defun-function #'css-current-defun-n= ame)
+ =A0(smie-setup css-smie-grammar #'css-smie-rules)
=A0 =A0(when css-electric-keys
=A0 =A0 =A0(let ((fc (make-char-table 'auto-fill-chars)))
=A0 =A0 =A0 =A0(set-char-table-parent fc auto-fill-chars)
@@ -355,131 +371,6 @@
=A0 =A0 =A0 =A0 =A0 =A0 =A0;; Don't use the default filling code.
=A0 =A0 =A0 =A0 =A0 =A0 =A0t)))))))

-;;; Navigation and indentation.
-
-(defconst css-navigation-syntax-table
- =A0(let ((st (make-syntax-table css-mode-syntax-table)))
- =A0 =A0(map-char-table (lambda (c v)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0;; Turn punctuation (code =3D = 1) into symbol (code =3D 1).
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (eq (car-safe v) 1)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(set-char-table-range = st c (cons 3 (cdr v)))))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0st)
- =A0 =A0st))
-
-(defun css-backward-sexp (n)
- =A0(let ((forward-sexp-function nil))
- =A0 =A0(if (< n 0) (css-forward-sexp (- n))
- =A0 =A0 =A0(while (> n 0)
- =A0 =A0 =A0 =A0(setq n (1- n))
- =A0 =A0 =A0 =A0(forward-comment (- (point-max)))
- =A0 =A0 =A0 =A0(if (not (eq (char-before) ?\;))
- =A0 =A0 =A0 =A0 =A0 =A0(backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0(while (progn (backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-comment (- (p= oint-max)))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0;; FIXME: We should al= so skip punctuation.
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(not (or (bobp) (memq = (char-before) '(?\; ?\{))))))))))))
-
-(defun css-forward-sexp (n)
- =A0(let ((forward-sexp-function nil))
- =A0 =A0(if (< n 0) (css-backward-sexp (- n))
- =A0 =A0 =A0(while (> n 0)
- =A0 =A0 =A0 =A0(setq n (1- n))
- =A0 =A0 =A0 =A0(forward-comment (point-max))
- =A0 =A0 =A0 =A0(if (not (eq (char-after) ?\;))
- =A0 =A0 =A0 =A0 =A0 =A0(forward-sexp 1)
- =A0 =A0 =A0 =A0 =A0(while (progn (forward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-comment (poin= t-max))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0;; FIXME: We should al= so skip punctuation.
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(not (memq (char-after= ) '(?\; ?\})))))))))))
-
-(defun css-indent-calculate-virtual ()
- =A0(if (or (save-excursion (skip-chars-backward " \t") (bolp))<= br> - =A0 =A0 =A0 =A0 =A0(if (looking-at "\\s(")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-char 1) (skip-chars-forward "= ; \t")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(not (or (eolp) (looking-at comment-start-= skip))))))
- =A0 =A0 =A0(current-column)
- =A0 =A0(css-indent-calculate)))
-
-(defcustom css-indent-offset 4
- =A0"Basic size of one indentation step."
- =A0:version "22.2"
- =A0:type 'integer
- =A0:group 'css)
-
-(defun css-indent-calculate ()
- =A0(let ((ppss (syntax-ppss))
- =A0 =A0 =A0 =A0pos)
- =A0 =A0(with-syntax-table css-navigation-syntax-table
- =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0(cond
- =A0 =A0 =A0 =A0 ;; Inside a string.
- =A0 =A0 =A0 =A0 ((nth 3 ppss) 'noindent)
- =A0 =A0 =A0 =A0 ;; Inside a comment.
- =A0 =A0 =A0 =A0 ((nth 4 ppss)
- =A0 =A0 =A0 =A0 =A0(setq pos (point))
- =A0 =A0 =A0 =A0 =A0(forward-line -1)
- =A0 =A0 =A0 =A0 =A0(skip-chars-forward " \t")
- =A0 =A0 =A0 =A0 =A0(if (>=3D (nth 8 ppss) (point))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0(progn
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(goto-char (nth 8 ppss))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (eq (char-after pos) ?*)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-char 1)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (not (looking-at comment-start-ski= p))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(error "Internal css-mode= error")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(goto-char (match-end 0))))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(current-column))
- =A0 =A0 =A0 =A0 =A0 =A0(if (and (eq (char-after pos) ?*) (eq (char-after)= ?*))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(current-column)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0;; 'noindent
- =A0 =A0 =A0 =A0 =A0 =A0 =A0(current-column)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0)))
- =A0 =A0 =A0 =A0 ;; In normal code.
- =A0 =A0 =A0 =A0 (t
- =A0 =A0 =A0 =A0 =A0(or
- =A0 =A0 =A0 =A0 =A0 (when (looking-at "\\s)")
- =A0 =A0 =A0 =A0 =A0 =A0 (forward-char 1)
- =A0 =A0 =A0 =A0 =A0 =A0 (backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate-virtual))
- =A0 =A0 =A0 =A0 =A0 (when (looking-at comment-start-skip)
- =A0 =A0 =A0 =A0 =A0 =A0 (forward-comment (point-max))
- =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate))
- =A0 =A0 =A0 =A0 =A0 (when (save-excursion (forward-comment (- (point-max)= ))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (setq pos= (point))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (eq (char= -syntax (preceding-char)) ?\())
- =A0 =A0 =A0 =A0 =A0 =A0 (goto-char (1- pos))
- =A0 =A0 =A0 =A0 =A0 =A0 (if (not (looking-at "\\s([ \t]*"))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (error "Internal css-mode error"= ;)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 (if (or (memq (char-after (match-end 0)) '= ;(?\n nil))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (save-excursion (goto-char (m= atch-end 0))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 (looking-at comment-start-skip)))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (+ (css-indent-calculate-virtual) css= -indent-offset)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (progn (goto-char (match-end 0)) (current= -column)))))
- =A0 =A0 =A0 =A0 =A0 (progn
- =A0 =A0 =A0 =A0 =A0 =A0 (css-backward-sexp 1)
- =A0 =A0 =A0 =A0 =A0 =A0 (if (looking-at "\\s(")
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 (css-indent-calculate-virtual))))))))))
-
-
-(defun css-indent-line ()
- =A0"Indent current line according to CSS indentation rules." - =A0(interactive)
- =A0(let* ((savep (point))
- =A0 =A0 =A0 =A0 (forward-sexp-function nil)
- =A0 =A0 =A0 =A0(indent (condition-case nil
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(save-excursion
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(forward-line 0)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(skip-chars-forward " \t&= quot;)
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(if (>=3D (point) savep) (s= etq savep nil))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(css-indent-calculate))
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(error nil))))
- =A0 =A0(if (not (numberp indent)) 'noindent
- =A0 =A0 =A0(if savep
- =A0 =A0 =A0 =A0 =A0(save-excursion (indent-line-to indent))
- =A0 =A0 =A0 =A0(indent-line-to indent)))))

=A0(defun css-current-defun-name ()
=A0 =A0"Return the name of the CSS section at point, or nil."



--20cf3074d594ecfdb804d370ebd7--