extra/haml-mode.el in haml-edge-2.1.44 vs extra/haml-mode.el in haml-edge-2.1.45

- old
+ new

@@ -59,12 +59,16 @@ if the next line could be nested within this line. 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" "begin" "elsif" "rescue" "ensure" "when")) "\\)") @@ -120,57 +124,77 @@ %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) + (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 tag, classes, and ids + (while (haml-move "\\([.#%]\\)[a-z0-9_:\\-]*") + (put-text-property (match-beginning 0) (match-end 0) 'face + (case (char-after (match-beginning 1)) + (?% font-lock-function-name-face) + (?# font-lock-keyword-face) + (?. font-lock-type-face)))) - ;; Highlight obj refs - (when (eq (char-after) ?\[) - (let ((beg (point))) - (haml-limited-forward-sexp eol) - (haml-fontify-region-as-ruby beg (point)))) + (block loop + (while t + (let ((eol (save-excursion (end-of-line) (point)))) + (case (char-after) + ;; Highlight obj refs + (?\[ + (let ((beg (point))) + (haml-limited-forward-sexp eol) + (haml-fontify-region-as-ruby beg (point)))) + ;; Highlight new attr hashes + (?\( + (forward-char 1) + (while + (and (haml-parse-new-attr-hash + (lambda (type beg end) + (case type + (name (put-text-property beg end 'face font-lock-constant-face)) + (value (haml-fontify-region-as-ruby beg end))))) + (not (eobp))) + (forward-line 1) + (beginning-of-line))) + ;; Highlight old attr hashes + (?\{ + (let ((beg (point))) + (haml-limited-forward-sexp eol) - ;; Highlight attr hashes - (when (eq (char-after) ?\{) - (let ((beg (point))) - (haml-limited-forward-sexp eol) + ;; Check for multiline + (while (and (eolp) (eq (char-before) ?,) (not (eobp))) + (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)))) - ;; Check for multiline - (while (and (eolp) (eq (char-before) ?,) (not (eobp))) - (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 (+ 1 beg) (point)))) + (t (return-from loop)))))) - (haml-fontify-region-as-ruby (+ 1 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)) - ;; 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-move (re) + "Try matching and moving to the end of a regular expression." + (when (looking-at re) + (goto-char (match-end 0)) + t)) (defun haml-highlight-interpolation (limit) "Highlight Ruby interpolation (#{foo})." (when (re-search-forward "\\(#{\\)" limit t) (save-match-data @@ -439,14 +463,12 @@ (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) + (if (haml-unclosed-attr-hash-p) (cdr (assq 'hash-indent attr-props)) (list (+ (cdr (assq 'indent attr-props)) haml-indent-offset) nil))))) (loop for opener in haml-block-openers if (looking-at opener) return t finally return nil)) @@ -462,23 +484,54 @@ 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_:\\-]+\\)+{") + (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 - (if (eq (char-before) ?,) - `((indent . ,(current-indentation)) - (hash-indent . ,(- (match-end 0) (match-beginning 0))) - (point . ,(match-beginning 0))) - nil))) + (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) - (end-of-line) - (when (not (eq (char-before) ?,)) - (return-from haml-parse-multiline-attr-hash nil)))))) + (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)))) + (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." (save-excursion (beginning-of-line)