Initial Commit
[packages] / xemacs-packages / hm--html-menus / hm--html-indentation.el
1 ;;; hm--html-indentation.el
2 ;;; v1.00;  9-Feb-1997
3 ;;; Copyright (C) 1997 Heiko Muenkel
4 ;;; email: muenkel@tnt.uni-hannover.de
5 ;;;
6 ;;;  This program is free software; you can redistribute it and/or modify
7 ;;;  it under the terms of the GNU General Public License as published by
8 ;;;  the Free Software Foundation; either version 1, or (at your option)
9 ;;;  any later version.
10 ;;;
11 ;;;  This program is distributed in the hope that it will be useful,
12 ;;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;;  GNU General Public License for more details.
15 ;;;
16 ;;;  You should have received a copy of the GNU General Public License
17 ;;;  along with this program; if not, write to the Free Software
18 ;;;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ;;;
20 ;;; 
21 ;;; Description:
22 ;;;
23 ;;;     Defines functions for the indentation.
24 ;;; 
25 ;;; Installation: 
26 ;;;   
27 ;;;     Put this file in one of your load path directories.
28 ;;;
29
30 (defun hm--html-point-between-strings-p (string-1
31                                          string-2
32                                          &optional boundary)
33   "Returns non nil, if the current point is between STRING-1 and STRING-2."
34   (when (and (re-search-backward (concat "\\("
35                                          (regexp-quote string-1)
36                                          "\\)\\|\\("
37                                          (regexp-quote string-2)
38                                          "\\)")
39                                  boundary
40                                  t)
41              (match-string 1))
42     (point)))
43
44 (defun hm--html-in-comment-p ()
45   "Checks if the current point is in a comment block.
46 If this is the case, then the start point of the comment is returned.
47 Otherwise nil is returned."
48   (save-excursion
49     (hm--html-point-between-strings-p comment-start comment-end)))
50
51 (defun hm--html-previous-line-start ()
52   "Returns the start of the previous non blank line."
53   (save-excursion
54     (beginning-of-line)
55     (skip-chars-backward " \t\n")
56     (beginning-of-line)
57     (point)))
58
59 (defun hm--html-look-at-comment-end-p ()
60   "T, if the current line starts with the comment end."
61   (looking-at (regexp-quote comment-end)))
62
63 (defun hm--html-column-of-previous-regexp (regexp)
64   "Returns the column of the start of the previous REGEXP.
65 It searches backward until the REGEXP is found. If no
66 REGEXP is found, then it returns 0."
67   (save-excursion
68     (if (re-search-backward regexp nil t)
69         (current-column)
70       0)))
71
72 (defun hm--html-look-at-end-tag-p ()
73   "Returns the end tag name if the point is at the start of an end tag.
74 nil is returned otherwise."
75   (when (looking-at "\\(<[ \t\n]*/[ \t\n]*\\)\\([^ \t\n>]+\\)")
76     (match-string 2)))
77     
78
79 (defun hm--html-previous-line-indentation ()
80   "Returns the indentation of the previous non blank line."
81   (save-excursion
82     (beginning-of-line)
83     (skip-chars-backward " \t\n")
84     (back-to-indentation)
85     (current-column)))
86
87 (defun hm--html-in-tag-p ()
88   "Checks if the current point is in a tag.
89 If this is the case, then the start point of the tag is returned.
90 Otherwise nil is returned."
91   (save-excursion
92     (let ((start (re-search-backward "\\(<\\)\\|\\(>\\)" nil t)))
93       (when (match-string 1)
94         start))))
95
96 (defun hm--html-return-beginning-of-line ()
97   "Returns the beginning of the current line."
98   (save-excursion
99     (beginning-of-line)
100     (point)))
101
102 (defun hm--html-return-end-of-line ()
103   "Returns the end of the current line."
104   (save-excursion
105     (end-of-line)
106     (point)))
107
108 (defun hm--html-paramter-column-in-line-after-point (point)
109   "Returns the column where the second non blank text after POINT starts.
110 This point must be in the line with POINT otherwise it returns nil."
111   (save-excursion
112     (goto-char point)
113     (when (re-search-forward "<[ \t]*[^ \t]+[ \t]"
114                              (hm--html-return-end-of-line)
115                              t)
116       (when (looking-at "[^\n]")
117         (current-column)))))
118
119 (defun hm--html-column-of-point (point)
120   "Returns the column of the POINT."
121   (save-excursion
122     (goto-char point)
123     (current-column)))
124
125 (defun hm--html-search-previous-tag-in-current-line ()
126   "Searches tags from the `(point)' to the beginning of the line.
127 It returns nil, if there is no tag and the tag name, if there is
128 a tag. The tag name contains a leading /, if it is an end tag."
129   (when (re-search-backward ">" (hm--html-return-beginning-of-line) t)
130     (when (re-search-backward
131            "\\(<[ \t\n]*\\(/?\\)\\([ \t\n]*[^> \t\n]+\\)[^>]*\\)"
132            nil
133            t)
134       (concat (match-string 2) (match-string 3)))))
135
136 (defun hm--html-search-start-tag (tag-name until)
137   "Searches start tag backwards from the current point until the point UNTIL.
138 The name of the tag is TAG-NAME. After this function the point is at UNTIL
139  (then it returns nil) or at the start of the tag, then it returns t."
140   (if (re-search-backward (concat "\\(<[ \t\n]*\\)\\(/?\\)\\("
141                                   tag-name 
142                                   "\\)\\([^>]*>\\)") until t)
143       (if (string= "/" (match-string 2))
144           (progn
145             (hm--html-search-start-tag tag-name until)
146             (hm--html-search-start-tag tag-name until)) 
147         t)
148     (goto-char until)
149     nil))
150
151 (defun hm--html-is-one-element-tag-p (tag-name)
152   "Returns t, if the tag with the tag-name is a one element tag."
153   (assoc ':hm--html-one-element-tag
154          (cdr (assoc* (downcase tag-name)
155                       hm--html-tag-name-alist
156                       :test 'string=))))
157
158 (defun hm--html-calculate-indent-according-to-previous-tags ()
159   "Calculate the indent according to the previous tags in this line.
160 If no tags are found, then nil is returned."
161   (save-excursion
162     (let ((tag (hm--html-search-previous-tag-in-current-line)))
163       (cond ((not tag) nil)
164             
165             ((eq ?/ (elt tag 0)) ; end tag found
166              (if (hm--html-search-start-tag 
167                   (substring tag 1)
168                   (point-min))
169                  (or (hm--html-calculate-indent-according-to-previous-tags)
170                      (progn
171                        (backward-to-indentation 0)
172                        (current-column)))
173                0)) ; it may be that the current indentation is better here
174
175             ((hm--html-is-one-element-tag-p tag) ; one element tag
176              (or (hm--html-calculate-indent-according-to-previous-tags)
177                  (progn
178                    (backward-to-indentation 0)
179                    (current-column))))
180
181             (t ; start tag found
182              (+ (current-column) hm--html-inter-tag-indent))))))
183
184
185 (defun hm--html-calculate-indent ()
186   "Calculate the indentation of the current line."
187   (let ((match-point)
188         (tag))
189     (save-excursion
190       (beginning-of-line)
191       (back-to-indentation)
192       (cond ((eq (count-lines (point-min) (point)) 0) 0) ; Filestart
193             
194             ((setq match-point (hm--html-in-comment-p)) ; in a comment
195              (if (>= match-point (hm--html-previous-line-start)) ; 1. line
196                  (if (hm--html-look-at-comment-end-p)
197                      (hm--html-column-of-previous-regexp
198                       (regexp-quote comment-start))
199                    (+ (hm--html-column-of-previous-regexp
200                        (regexp-quote comment-start))
201                       hm--html-comment-indent))
202                (if (hm--html-look-at-comment-end-p)
203                    (- (hm--html-previous-line-indentation)
204                       hm--html-comment-indent)
205                  (hm--html-previous-line-indentation))))
206             
207             ((setq tag (hm--html-look-at-end-tag-p)) ; look at end tag
208              (hm--html-search-start-tag tag (point-min))
209              (current-column))
210             
211             ((looking-at ">")
212              (hm--html-column-of-previous-regexp "<"))
213
214             ((setq match-point (hm--html-in-tag-p))
215              (if (>= match-point (hm--html-previous-line-start)) ; 1. line
216                  (or (hm--html-paramter-column-in-line-after-point match-point)
217                      (+ (hm--html-column-of-point match-point)
218                         hm--html-intra-tag-indent))
219                (hm--html-previous-line-indentation)))
220
221             (t (or (save-excursion  ; check previous line
222                      (skip-chars-backward " \t\n")
223                      (hm--html-calculate-indent-according-to-previous-tags))
224                    (hm--html-previous-line-indentation)))
225             ))))
226
227
228 ;;; Indentation commands
229
230 (defun hm--html-indent-line ()
231   "Indent the current line line."
232   (interactive)
233   (unless hm--html-disable-indentation
234     (let ((pos (- (point-max) (point))))
235       (indent-line-to (max 0 (hm--html-calculate-indent)))
236       (when (> (- (point-max) pos) (point))
237         (goto-char (- (point-max) pos))))))
238
239 ;(defun hm--html-indent-region (begin end)
240 ;  "Indents the region between BEGIN and END according to the major mode."
241 ;  (interactive "d\nm")
242 ;  (when (< end begin)
243 ;    (let ((a end))
244 ;      (setq end begin)
245 ;      (setq begin a)))
246 ;  (save-excursion
247 ;    (goto-char begin)
248 ;    (let ((old-point))
249 ;      (while (and (<= (point) end)
250 ;                 (not (eq (point) old-point)))
251 ;       (setq old-point (point))
252 ;       (indent-according-to-mode)
253 ;       (forward-line)
254 ;       ))))
255
256 (defun hm--html-indent-region (begin end)
257   "Indents the region between BEGIN and END according to the major mode."
258   (interactive "d\nm")
259   (when (< end begin)
260     (let ((a end))
261       (setq end begin)
262       (setq begin a)))
263   (let ((lines (count-lines begin end)))
264     (save-excursion
265       (goto-char begin)
266       (loop repeat lines
267             do (indent-according-to-mode)
268             (forward-line))))
269   )
270
271 (provide 'hm--html-indentation)