;;; hm--html-indentation.el ;;; v1.00; 9-Feb-1997 ;;; Copyright (C) 1997 Heiko Muenkel ;;; email: muenkel@tnt.uni-hannover.de ;;; ;;; This program is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 1, or (at your option) ;;; any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; ;;; ;;; Description: ;;; ;;; Defines functions for the indentation. ;;; ;;; Installation: ;;; ;;; Put this file in one of your load path directories. ;;; (defun hm--html-point-between-strings-p (string-1 string-2 &optional boundary) "Returns non nil, if the current point is between STRING-1 and STRING-2." (when (and (re-search-backward (concat "\\(" (regexp-quote string-1) "\\)\\|\\(" (regexp-quote string-2) "\\)") boundary t) (match-string 1)) (point))) (defun hm--html-in-comment-p () "Checks if the current point is in a comment block. If this is the case, then the start point of the comment is returned. Otherwise nil is returned." (save-excursion (hm--html-point-between-strings-p comment-start comment-end))) (defun hm--html-previous-line-start () "Returns the start of the previous non blank line." (save-excursion (beginning-of-line) (skip-chars-backward " \t\n") (beginning-of-line) (point))) (defun hm--html-look-at-comment-end-p () "T, if the current line starts with the comment end." (looking-at (regexp-quote comment-end))) (defun hm--html-column-of-previous-regexp (regexp) "Returns the column of the start of the previous REGEXP. It searches backward until the REGEXP is found. If no REGEXP is found, then it returns 0." (save-excursion (if (re-search-backward regexp nil t) (current-column) 0))) (defun hm--html-look-at-end-tag-p () "Returns the end tag name if the point is at the start of an end tag. nil is returned otherwise." (when (looking-at "\\(<[ \t\n]*/[ \t\n]*\\)\\([^ \t\n>]+\\)") (match-string 2))) (defun hm--html-previous-line-indentation () "Returns the indentation of the previous non blank line." (save-excursion (beginning-of-line) (skip-chars-backward " \t\n") (back-to-indentation) (current-column))) (defun hm--html-in-tag-p () "Checks if the current point is in a tag. If this is the case, then the start point of the tag is returned. Otherwise nil is returned." (save-excursion (let ((start (re-search-backward "\\(<\\)\\|\\(>\\)" nil t))) (when (match-string 1) start)))) (defun hm--html-return-beginning-of-line () "Returns the beginning of the current line." (save-excursion (beginning-of-line) (point))) (defun hm--html-return-end-of-line () "Returns the end of the current line." (save-excursion (end-of-line) (point))) (defun hm--html-paramter-column-in-line-after-point (point) "Returns the column where the second non blank text after POINT starts. This point must be in the line with POINT otherwise it returns nil." (save-excursion (goto-char point) (when (re-search-forward "<[ \t]*[^ \t]+[ \t]" (hm--html-return-end-of-line) t) (when (looking-at "[^\n]") (current-column))))) (defun hm--html-column-of-point (point) "Returns the column of the POINT." (save-excursion (goto-char point) (current-column))) (defun hm--html-search-previous-tag-in-current-line () "Searches tags from the `(point)' to the beginning of the line. It returns nil, if there is no tag and the tag name, if there is a tag. The tag name contains a leading /, if it is an end tag." (when (re-search-backward ">" (hm--html-return-beginning-of-line) t) (when (re-search-backward "\\(<[ \t\n]*\\(/?\\)\\([ \t\n]*[^> \t\n]+\\)[^>]*\\)" nil t) (concat (match-string 2) (match-string 3))))) (defun hm--html-search-start-tag (tag-name until) "Searches start tag backwards from the current point until the point UNTIL. The name of the tag is TAG-NAME. After this function the point is at UNTIL (then it returns nil) or at the start of the tag, then it returns t." (if (re-search-backward (concat "\\(<[ \t\n]*\\)\\(/?\\)\\(" tag-name "\\)\\([^>]*>\\)") until t) (if (string= "/" (match-string 2)) (progn (hm--html-search-start-tag tag-name until) (hm--html-search-start-tag tag-name until)) t) (goto-char until) nil)) (defun hm--html-is-one-element-tag-p (tag-name) "Returns t, if the tag with the tag-name is a one element tag." (assoc ':hm--html-one-element-tag (cdr (assoc* (downcase tag-name) hm--html-tag-name-alist :test 'string=)))) (defun hm--html-calculate-indent-according-to-previous-tags () "Calculate the indent according to the previous tags in this line. If no tags are found, then nil is returned." (save-excursion (let ((tag (hm--html-search-previous-tag-in-current-line))) (cond ((not tag) nil) ((eq ?/ (elt tag 0)) ; end tag found (if (hm--html-search-start-tag (substring tag 1) (point-min)) (or (hm--html-calculate-indent-according-to-previous-tags) (progn (backward-to-indentation 0) (current-column))) 0)) ; it may be that the current indentation is better here ((hm--html-is-one-element-tag-p tag) ; one element tag (or (hm--html-calculate-indent-according-to-previous-tags) (progn (backward-to-indentation 0) (current-column)))) (t ; start tag found (+ (current-column) hm--html-inter-tag-indent)))))) (defun hm--html-calculate-indent () "Calculate the indentation of the current line." (let ((match-point) (tag)) (save-excursion (beginning-of-line) (back-to-indentation) (cond ((eq (count-lines (point-min) (point)) 0) 0) ; Filestart ((setq match-point (hm--html-in-comment-p)) ; in a comment (if (>= match-point (hm--html-previous-line-start)) ; 1. line (if (hm--html-look-at-comment-end-p) (hm--html-column-of-previous-regexp (regexp-quote comment-start)) (+ (hm--html-column-of-previous-regexp (regexp-quote comment-start)) hm--html-comment-indent)) (if (hm--html-look-at-comment-end-p) (- (hm--html-previous-line-indentation) hm--html-comment-indent) (hm--html-previous-line-indentation)))) ((setq tag (hm--html-look-at-end-tag-p)) ; look at end tag (hm--html-search-start-tag tag (point-min)) (current-column)) ((looking-at ">") (hm--html-column-of-previous-regexp "<")) ((setq match-point (hm--html-in-tag-p)) (if (>= match-point (hm--html-previous-line-start)) ; 1. line (or (hm--html-paramter-column-in-line-after-point match-point) (+ (hm--html-column-of-point match-point) hm--html-intra-tag-indent)) (hm--html-previous-line-indentation))) (t (or (save-excursion ; check previous line (skip-chars-backward " \t\n") (hm--html-calculate-indent-according-to-previous-tags)) (hm--html-previous-line-indentation))) )))) ;;; Indentation commands (defun hm--html-indent-line () "Indent the current line line." (interactive) (unless hm--html-disable-indentation (let ((pos (- (point-max) (point)))) (indent-line-to (max 0 (hm--html-calculate-indent))) (when (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos)))))) ;(defun hm--html-indent-region (begin end) ; "Indents the region between BEGIN and END according to the major mode." ; (interactive "d\nm") ; (when (< end begin) ; (let ((a end)) ; (setq end begin) ; (setq begin a))) ; (save-excursion ; (goto-char begin) ; (let ((old-point)) ; (while (and (<= (point) end) ; (not (eq (point) old-point))) ; (setq old-point (point)) ; (indent-according-to-mode) ; (forward-line) ; )))) (defun hm--html-indent-region (begin end) "Indents the region between BEGIN and END according to the major mode." (interactive "d\nm") (when (< end begin) (let ((a end)) (setq end begin) (setq begin a))) (let ((lines (count-lines begin end))) (save-excursion (goto-char begin) (loop repeat lines do (indent-according-to-mode) (forward-line)))) ) (provide 'hm--html-indentation)