 The function can also return a positive integer to indicate
 a specific level to which the current line could be indented.")
 
+(defconst haml-tag-beg-re
+  "^ *\\(?:[%\\.#][a-z0-9_:\\-]*\\)+\\(?:(.*)\\|{.*}\\|\\[.*\\]\\)*"
+  "A regexp matching the beginning of a Haml tag, through (), {}, and [].")
+
 (defvar haml-block-openers
-  `("^ *\\([%\\.#][a-z0-9_:\\-]*\\)+\\({.*}\\)?\\(\\[.*\\]\\)?[><]*[ \t]*$"
+  `(,(concat haml-tag-beg-re "[><]*[ \t]*$")
     "^ *[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
     ,(concat "^ *[&!]?[-=~][ \t]*\\("
              (regexp-opt '("if" "unless" "while" "until" "else"

  "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)
          (list (+ (cdr (assq 'indent attr-props)) haml-indent-offset) nil)))))
  (loop for opener in haml-block-openers
        if (looking-at opener) return t

  (save-excursion
    (while t
      (beginning-of-line)
      (if (looking-at (eval-when-compile (concat haml-tag-beg-re "\\([{(]\\)")))
          (progn
            (goto-char (- (match-end 0) 1))
            (haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
            (return-from haml-parse-multiline-attr-hash
              (when (or (string-equal (match-string 1) "(") (eq (char-before) ?,))
                `((indent . ,(current-indentation))
                  (hash-indent . ,(- (match-end 0) (match-beginning 0)))
                  (point . ,(match-beginning 0))))))
        (when (bobp) (return-from haml-parse-multiline-attr-hash))
        (forward-line -1)
        (unless (haml-unclosed-attr-hash-p)
          (return-from haml-parse-multiline-attr-hash))))))

(defun* haml-unclosed-attr-hash-p ()
  "Return t if this line has an unclosed attribute hash, new or old."
  (save-excursion
    (end-of-line)
    (when (eq (char-before) ?,) (return-from haml-unclosed-attr-hash-p t))
    (re-search-backward "(\\|^")
    (haml-move "(")
    (haml-parse-new-attr-hash)))  

(defun* haml-parse-new-attr-hash (&optional (fn (lambda (type beg end) ())))
  "Parse a new-style attribute hash on this line, and returns
t if it's not finished on the current line.

FN should take three parameters: TYPE, BEG, and END.
TYPE is the type of text parsed ('name or 'value)
and BEG and END delimit that text in the buffer."
  (let ((eol (save-excursion (end-of-line) (point))))
    (save-excursion
      (while (not (haml-move ")"))
        (haml-move " *")
        (unless (haml-move "[a-z0-9_:\\-]+")
          (return-from haml-parse-new-attr-hash (haml-move " *$")))
        (funcall fn 'name (match-beginning 0) (match-end 0))
        (haml-move " *")
        (when (haml-move "=")
          (haml-move " *")
          (unless (looking-at "[\"'@a-z]") (return-from haml-parse-new-attr-hash))
          (let ((beg (point)))
            (haml-limited-forward-sexp eol)
            (funcall fn 'value beg (point)))
          (haml-move " *")))
      nil)))

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