 
 ;; Font lock
 
-(defun haml-nested-regexp (re)
-  (concat "^\\( *\\)" re "\\(\n\\(?:\\(?:\\1 .*\\| *\\)\n\\)*\\(?:\\1 .*\\| *\\)?\\)?"))
-
 (defconst haml-font-lock-keywords
-  `((,(haml-nested-regexp "\\(?:-#\\|/\\).*")  0 font-lock-comment-face)
-    (,(haml-nested-regexp ":\\w+") 0 font-lock-string-face)
-    (haml-highlight-interpolation  1 font-lock-variable-name-face prepend)
-    (haml-highlight-ruby-tag       1 font-lock-preprocessor-face)
-    (haml-highlight-ruby-script    1 font-lock-preprocessor-face)
-    ("^ *\\(\t\\)"                 1 'haml-tab-face)
-    ("^!!!.*"                      0 font-lock-constant-face)
-    ("| *$"                        0 font-lock-string-face)))
-
-(defconst haml-filter-re "^ *:\\w+")
-(defconst haml-comment-re "^ *\\(?:-\\#\\|/\\)")
-
-(defun haml-fontify-region-as-ruby (beg end)
-  "Use Ruby's font-lock variables to fontify the region between BEG and END."
-  (save-excursion
-    (save-match-data
-      (let ((font-lock-keywords ruby-font-lock-keywords)
-            (font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords)
-            font-lock-keywords-only
-            font-lock-extend-region-functions
-            font-lock-keywords-case-fold-search)
-        ;; font-lock-fontify-region apparently isn't inclusive,
-        ;; so we have to move the beginning back one char
-        (font-lock-fontify-region (- beg 1) end)))))
-
-(defun haml-highlight-ruby-script (limit)
-  "Highlight a Ruby script expression (-, =, or ~)."
-  (when (re-search-forward "^ *\\(-\\|[&!]?[=~]\\) \\(.*\\)$" limit t)
-    (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))))
-
-(defun haml-highlight-ruby-tag (limit)
-  "Highlight Ruby code within a Haml tag.
-
-This highlights the tag attributes and object refs of the tag,
-as well as the script expression (-, =, or ~) following the tag.
-
-For example, this will highlight all of the following:
-  %p{:foo => 'bar'}
-  %p[@bar]
-  %p= 'baz'
-  %p{:foo => 'bar'}[@bar]= 'baz'"
-  (when (re-search-forward "^ *[%.#]" limit t)
-    (let ((eol (save-excursion (end-of-line) (point))))
-      (forward-char -1)
-
-      ;; Highlight tag, classes, and ids
-      (while (looking-at "[.#%][a-z0-9_:\\-]*")
-        (put-text-property (match-beginning 0) (match-end 0) 'face
-                           (case (char-after)
-                             (?% font-lock-function-name-face)
-                             (?# font-lock-keyword-face)
-                             (?. font-lock-type-face)))
-        (goto-char (match-end 0)))
-
-      ;; Highlight obj refs
-      (when (eq (char-after) ?\[)
-        (let ((beg (point)))
-          (haml-limited-forward-sexp eol)
-          (haml-fontify-region-as-ruby beg (point))))
-
-      ;; Highlight attr hashes
-      (when (eq (char-after) ?\{)
-        (let ((beg (+ 1 (point))))
-          (haml-limited-forward-sexp eol)
-
-          ;; Check for multiline
-          (while (and (eolp) (eq (char-before) ?,))
-            (forward-line)
-            (let ((eol (save-excursion (end-of-line) (point))))
-              ;; If no sexps are closed,
-              ;; we're still continuing a  multiline hash
-              (if (>= (car (parse-partial-sexp (point) eol)) 0)
-                  (end-of-line)
-                ;; If sexps have been closed,
-                ;; set the point at the end of the total sexp
-                (goto-char beg)
-                (haml-limited-forward-sexp eol))))
-
-          (haml-fontify-region-as-ruby beg (point))))
-
-      ;; Move past end chars
-      (when (looking-at "[<>&!]+") (goto-char (match-end 0)))
-      ;; Highlight script
-      (if (looking-at "\\([=~]\\) \\(.*\\)$")
-          (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
-        ;; Give font-lock something to highlight
-        (forward-char -1)
-        (looking-at "\\(\\)"))
-      t)))
-
-(defun haml-highlight-interpolation (limit)
-  "Highlight Ruby interpolation (#{foo})."
-  (when (re-search-forward "\\(#{\\)" limit t)
-    (save-match-data
-      (forward-char -1)
-      (let ((beg (point)))
-        (haml-limited-forward-sexp limit)
-        (haml-fontify-region-as-ruby (+ 1 beg) (point)))
-
-      (when (eq (char-before) ?})
-        (put-text-property (- (point) 1) (point)
-                           'face font-lock-variable-name-face))
-      t)))
-
-(defun haml-limited-forward-sexp (limit &optional arg)
-  "Move forward using `forward-sexp' or to limit,
-whichever comes first."
-  (let (forward-sexp-function)
-    (condition-case err
-        (save-restriction
-          (narrow-to-region (point) limit)
-          (forward-sexp arg))
-      (scan-error
-       (unless (equal (nth 1 err) "Unbalanced parentheses")
-         (signal 'scan-error (cdr err)))
-       (goto-char limit)))))
-
-(defun* haml-extend-region-filters-comments ()
-  "Extend the font-lock region to encompass filters and comments."
-  (let ((old-beg font-lock-beg)
-        (old-end font-lock-end))
-    (save-excursion
-      (goto-char font-lock-beg)
-      (beginning-of-line)
-      (unless (or (looking-at haml-filter-re)
-                  (looking-at haml-comment-re))
-        (return-from haml-extend-region-filters-comments))
-      (setq font-lock-beg (point))
-      (haml-forward-sexp)
-      (beginning-of-line)
-      (setq font-lock-end (max font-lock-end (point))))
-    (or (/= old-beg font-lock-beg)
-        (/= old-end font-lock-end))))
-
-(defun* haml-extend-region-multiline-hashes ()
-  "Extend the font-lock region to encompass multiline attribute hashes."
-  (let ((old-beg font-lock-beg)
-        (old-end font-lock-end))
-    (save-excursion
-      (goto-char font-lock-beg)
-      (let ((attr-props (haml-parse-multiline-attr-hash))
-            multiline-end)
-        (when attr-props
-          (setq font-lock-beg (cdr (assq 'point attr-props)))
-
-          (end-of-line)
-          ;; Move through multiline attrs
-          (when (eq (char-before) ?,)
-            (save-excursion
-              (while (progn (end-of-line) (eq (char-before) ?,))
-                (forward-line))
-
-              (forward-line -1)
-              (end-of-line)
-              (setq multiline-end (point))))
-
-          (goto-char (+ (cdr (assq 'point attr-props))
-                        (cdr (assq 'hash-indent attr-props))
-                        -1))
-          (haml-limited-forward-sexp
-           (or multiline-end
-               (save-excursion (end-of-line) (point))))
-          (setq font-lock-end (max font-lock-end (point))))))
-    (or (/= old-beg font-lock-beg)
-        (/= old-end font-lock-end))))
-
+  '(("^ *\\(\t\\)"               1 'haml-tab-face)
+    ("^!!!.*"                    0 font-lock-constant-face)
+    ("\\('[^']*'\\)"             1 font-lock-string-face append)
+    ("\\(\"[^\"]*\"\\)"          1 font-lock-string-face append)
+    ("&?:\\w+"                   0 font-lock-constant-face append)
+    ("@[a-z0-9_]+"               0 font-lock-variable-name-face append)
+    ("| *$"                      0 font-lock-string-face)
+    ("^[ \t]*\\(/.*\\)$"         1 font-lock-comment-face append)
+    ("^[ \t]*\\(-#.*\\)$"         1 font-lock-comment-face prepend)
+    ("^ *\\(#[a-z0-9_]+\/?\\)"   1 font-lock-keyword-face)
+    ("^ *\\(\\.[a-z0-9_]+\/?\\)" 1 font-lock-type-face)
+    ("^ *\\(%[a-z0-9_]+\/?\\)"   1 font-lock-function-name-face)
+    ("^ *\\(#[a-z0-9_]+\/?\\)"   (1 font-lock-keyword-face)
+     ("\\.[a-z0-9_]+" nil nil    (0 font-lock-type-face)))
+    ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face)
+     ("\\.[a-z0-9_]+" nil nil    (0 font-lock-type-face)))
+    ("^ *\\(\\.[a-z0-9_]+\/?\\)" (1 font-lock-type-face)
+     ("\\#[a-z0-9_]+" nil nil    (0 font-lock-keyword-face)))
+    ("^ *\\(%[a-z0-9_]+\/?\\)"   (1 font-lock-function-name-face)
+     ("\\.[a-z0-9_]+" nil nil    (0 font-lock-type-face)))
+    ("^ *\\(%[a-z0-9_]+\/?\\)"   (1 font-lock-function-name-face)
+     ("\\#[a-z0-9_]+" nil nil    (0 font-lock-keyword-face)))
+;;    ("^ *\\([~=-] .*\\)"         1 font-lock-preprocessor-face prepend)
+    ("^ *[\\.#%a-z0-9_]+\\([~=-] .*\\)"     1 font-lock-preprocessor-face prepend)
+    ("^ *[\\.#%a-z0-9_]+\\({[^}]+}\\)"      1 font-lock-preprocessor-face prepend)
+    ("^ *[\\.#%a-z0-9_]+\\(\\[[^]]+\\]\\)"  1 font-lock-preprocessor-face prepend)))
 
 ;; Mode setup
 


;; Indentation and electric keys

(defun* haml-indent-p ()
  "Returns t if the current line can have lines nested beneath it."
  (let ((attr-props (haml-parse-multiline-attr-hash)))
    (when attr-props
      (end-of-line)
      (return-from haml-indent-p
        (if (eq (char-before) ?,) (cdr (assq 'hash-indent attr-props))
          (beginning-of-line)
          (+ (cdr (assq 'indent attr-props)) haml-indent-offset)))))
  (loop for opener in haml-block-openers
        if (looking-at opener) return t
        finally return nil))

(defun* haml-parse-multiline-attr-hash ()
  "Parses a multiline attribute hash, and returns
an alist with the following keys:

INDENT is the indentation of the line beginning the hash.

HASH-INDENT is the indentation of the first character
within the attribute hash.

POINT is the character position at the beginning of the line
beginning the hash."
  (save-excursion
    (while t
      (beginning-of-line)
      (if (looking-at "^ *\\(?:[.#%][a-z0-9_:\\-]+\\)+{")
          (progn
            (goto-char (- (match-end 0) 1))
            (haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
            (return-from haml-parse-multiline-attr-hash
              (if (eq (char-before) ?,)
                  `((indent . ,(current-indentation))
                    (hash-indent . ,(- (match-end 0) (match-beginning 0)))
                    (point . ,(match-beginning 0)))
                nil)))
        (forward-line -1)
        (end-of-line)
        (when (not (eq (char-before) ?,))
          (return-from haml-parse-multiline-attr-hash nil))))))

(defun haml-compute-indentation ()
  "Calculate the maximum sensible indentation for the current line."

  :group 'haml)

(defvar haml-indent-function 'haml-indent-p
  "This function should look at the current line and return true
if the next line could be nested within this line.")

;; TODO Allow "=\sThen some code" instead of no space.
(defvar haml-block-openers
  `("^ *\\([%\\.#][^ \t]*\\)\\(\\[.*\\]\\)?\\({.*}\\)?\\(\\[.*\\]\\)?[ \t]*$"
    "^ *[-=].*do[ \t]*\\(|.*|[ \t]*\\)?$"
    ,(concat "^ *-[ \t]*\\("
             (regexp-opt '("if" "unless" "while" "until" "else"
                           "begin" "elsif" "rescue" "ensure" "when"))
             "\\)")
