Mercurial > emacs
annotate lisp/textmodes/xml-lite.el @ 44168:68fd324f9f0f
(xml-lite-at-indentation-p): Move.
(xml-lite-in-string-p, xml-lite-looking-back-at, xml-lite-looking-at):
New functions.
(forward-xml-tag, backward-xml-tag, beginning-of-xml-tag)
(end-of-xml-tag): Remove.
(xml-lite-get-context): Better handling of comments.
(xml-lite-calculate-indent): Use xml-lite-in-string-p.
(xml-lite-parse-tag-backward): Rewrite.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Wed, 27 Mar 2002 00:06:19 +0000 |
parents | 055adf3412f7 |
children | e7a365c909ff |
rev | line source |
---|---|
43687 | 1 ;;; xml-lite.el --- an indentation-engine for XML |
2 | |
3 ;; Copyright (C) 2001 Mike Williams <mdub@bigfoot.com> | |
4 | |
5 ;; Author: Mike Williams <mdub@bigfoot.com> | |
6 ;; Created: February 2001 | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
7 ;; Version: $Revision: 1.28 $ |
43687 | 8 ;; Keywords: xml |
9 | |
10 ;; This file is part of GNU Emacs. | |
11 | |
12 ;; This program is free software; you can redistribute it and/or modify | |
13 ;; it under the terms of the GNU General Public License as published by | |
14 ;; the Free Software Foundation; either version 2 of the License, or | |
15 ;; (at your option) any later version. | |
16 ;; | |
17 ;; This program is distributed in the hope that it will be useful, | |
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 ;; GNU General Public License for more details. | |
21 ;; | |
22 ;; You should have received a copy of the GNU General Public License | |
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 ;; Boston, MA 02111-1307, USA. | |
26 | |
27 ;;; Commentary: | |
28 ;; | |
29 ;; This package provides a simple indentation engine for XML. It is | |
30 ;; intended for use in situations where the full power of the popular PSGML | |
31 ;; package (DTD parsing, syntax checking) is not required. | |
32 ;; | |
33 ;; xml-lite is designed to be used in conjunction with the default GNU | |
34 ;; Emacs sgml-mode, to provide a lightweight XML-editing environment. | |
35 ;; | |
36 ;; Updates will be made available at: | |
37 ;; http://www.bigfoot.com/~mdub/software/xml-lite.el | |
38 ;; | |
39 ;; Note: the font-lock support that was in this package has been removed. | |
40 | |
41 ;;; Thanks: | |
42 ;; | |
43 ;; Jens Schmidt <Jens.Schmidt@oracle.com> | |
44 ;; for his feedback and suggestions | |
45 | |
46 ;;; Code: | |
47 | |
48 (eval-when-compile (require 'cl)) | |
49 (require 'sgml-mode) | |
50 (require 'custom) | |
51 | |
52 | |
53 ;; Variables | |
54 | |
55 (defgroup xml-lite nil | |
56 "Customizable variables for XML-Lite mode." | |
57 :group 'languages | |
58 ) | |
59 | |
60 (defcustom xml-lite-indent-offset 4 | |
61 "*Specifies the default indentation level for `xml-lite-indent-line'." | |
62 :type 'integer | |
63 :group 'xml-lite | |
64 ) | |
65 | |
66 (defcustom xml-lite-indent-comment-offset 5 | |
67 "*Specifies the indentation level for XML comments." | |
68 :type 'integer | |
69 :group 'xml-lite | |
70 ) | |
71 | |
72 (defcustom xml-lite-electric-slash 'close | |
73 "*If non-nil, inserting a '/' after a '<' behaves electrically. | |
74 If set to `indent', typing '</' just triggers reindentation. | |
75 If set to `close', typing '</' inserts an end-tag for the | |
76 enclosing XML element." | |
77 :type '(choice (const :tag "Indent" indent) | |
78 | |
79 (const :tag "Close" close) | |
80 (const :tag "No" nil)) | |
81 | |
82 :group 'xml-lite | |
83 ) | |
84 | |
85 (defcustom xml-lite-mode-line-string " XML" | |
86 "*String to display in the modeline when `xml-lite-mode' is active. | |
87 Set this to nil if you don't want a modeline indicator for xml-lite-mode." | |
88 :type 'string | |
89 :group 'xml-lite) | |
90 | |
91 (defcustom xml-lite-mode-hook nil | |
92 "*Hook called by `xml-lite-mode'." | |
93 :type 'hook | |
94 :group 'xml-lite) | |
95 | |
96 ;;;###autoload | |
97 (defvar xml-lite-mode nil | |
98 "Non-nil if `xml-lite-mode' is enabled.") | |
99 (make-variable-buffer-local 'xml-lite-mode) | |
100 | |
101 | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
102 ;; Syntax analysis |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
103 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
104 (defsubst xml-lite-at-indentation-p () |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
105 "Return true if point is at the first non-whitespace character on the line." |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
106 (save-excursion |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
107 (skip-chars-backward " \t") |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
108 (bolp))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
109 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
110 (defun xml-lite-in-string-p (&optional limit) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
111 "Determine whether point is inside a string." |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
112 (let (syntax-info) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
113 (or limit |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
114 (setq limit (or (save-excursion |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
115 (re-search-backward "^[ \t]*<" nil t)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
116 (point-min)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
117 (setq syntax-info (parse-partial-sexp limit (point))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
118 (if (nth 3 syntax-info) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
119 (list (nth 3 syntax-info) (nth 8 syntax-info))))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
120 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
121 |
43687 | 122 ;; Parsing |
123 | |
124 (defstruct (xml-lite-tag | |
125 (:constructor xml-lite-make-tag (type start end name name-end))) | |
126 type start end name name-end) | |
127 | |
128 (defsubst xml-lite-parse-tag-name () | |
129 "Skip past a tag-name, and return the name." | |
130 (let ((here (point))) | |
131 (if (> (skip-chars-forward "-._:A-Za-z0-9") 0) | |
132 (buffer-substring-no-properties here (point))))) | |
133 | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
134 (defsubst xml-lite-looking-back-at (s) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
135 (let ((limit (max (- (point) (length s)) (point-min)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
136 (equal s (buffer-substring-no-properties limit (point))))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
137 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
138 (defsubst xml-lite-looking-at (s) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
139 (let ((limit (min (+ (point) (length s))))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
140 (equal s (buffer-substring-no-properties (point) limit)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
141 |
43687 | 142 (defun xml-lite-parse-tag-backward () |
143 "Get information about the parent tag." | |
144 (let ((limit (point)) | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
145 tag-type tag-start tag-end name name-end) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
146 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
147 (cond |
43687 | 148 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
149 ((null (re-search-backward "[<>]" nil t))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
150 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
151 ((= ?> (char-after)) ;--- found tag-end --- |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
152 (setq tag-end (1+ (point))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
153 (goto-char tag-end) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
154 (cond |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
155 ((xml-lite-looking-back-at "--") ; comment |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
156 (setq tag-type 'comment |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
157 tag-start (search-backward "<!--" nil t))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
158 ((xml-lite-looking-back-at "]]>") ; cdata |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
159 (setq tag-type 'cdata |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
160 tag-start (search-backward "![CDATA[" nil t))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
161 (t |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
162 (setq tag-start |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
163 (ignore-errors (backward-sexp) (point)))))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
164 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
165 ((= ?< (char-after)) ;--- found tag-start --- |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
166 (setq tag-start (point)) |
43687 | 167 (goto-char (1+ tag-start)) |
168 (cond | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
169 ((xml-lite-looking-at "!--") ; comment |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
170 (setq tag-type 'comment |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
171 tag-end (search-forward "-->" nil t))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
172 ((xml-lite-looking-at "![CDATA[") ; cdata |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
173 (setq tag-type 'cdata |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
174 tag-end (search-forward "]]>" nil t))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
175 (t |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
176 (goto-char tag-start) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
177 (setq tag-end |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
178 (ignore-errors (forward-sexp) (point)))))) |
43687 | 179 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
180 ) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
181 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
182 (cond |
43687 | 183 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
184 ((or tag-type (null tag-start))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
185 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
186 ((= ?! (char-after (1+ tag-start))) ; declaration |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
187 (setq tag-type 'decl)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
188 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
189 ((= ?? (char-after (1+ tag-start))) ; processing-instruction |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
190 (setq tag-type 'pi)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
191 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
192 ((= ?/ (char-after (1+ tag-start))) ; close-tag |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
193 (goto-char (+ 2 tag-start)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
194 (setq tag-type 'close |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
195 name (xml-lite-parse-tag-name) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
196 name-end (point))) |
43687 | 197 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
198 ((member ; JSP tags etc |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
199 (char-after (1+ tag-start)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
200 '(?% ?#)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
201 (setq tag-type 'unknown)) |
43687 | 202 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
203 (t |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
204 (goto-char (1+ tag-start)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
205 (setq tag-type 'open |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
206 name (xml-lite-parse-tag-name) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
207 name-end (point)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
208 ;; check whether it's an empty tag |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
209 (if (and tag-end (eq ?/ (char-before (- tag-end 1)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
210 (setq tag-type 'empty)))) |
43687 | 211 |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
212 (cond |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
213 (tag-start |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
214 (goto-char tag-start) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
215 (xml-lite-make-tag tag-type tag-start tag-end name name-end))))) |
43687 | 216 |
217 (defsubst xml-lite-inside-tag-p (tag-info &optional point) | |
218 "Return true if TAG-INFO contains the POINT." | |
219 (let ((end (xml-lite-tag-end tag-info)) | |
220 (point (or point (point)))) | |
221 (or (null end) | |
222 (> end point)))) | |
223 | |
224 (defun xml-lite-get-context (&optional full) | |
225 "Determine the context of the current position. | |
226 If FULL is non-nil, parse back to the beginning of the buffer, otherwise | |
227 parse until we find a start-tag as the first thing on a line. | |
228 | |
229 The context is a list of tag-info structures. The last one is the tag | |
230 immediately enclosing the current position." | |
231 (let ((here (point)) | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
232 (ignore-depth 0) |
43687 | 233 tag-info context) |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
234 ;; CONTEXT keeps track of the tag-stack |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
235 ;; IGNORE-DEPTH keeps track of the nesting level of point relative to the |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
236 ;; first (outermost) tag on the context. This is the number of |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
237 ;; enclosing start-tags we'll have to ignore. |
43687 | 238 (save-excursion |
239 | |
240 (while | |
241 (and (or (not context) | |
242 full | |
243 (not (xml-lite-at-indentation-p))) | |
244 (setq tag-info (xml-lite-parse-tag-backward))) | |
245 | |
246 (cond | |
247 | |
248 ;; inside a tag ... | |
249 ((xml-lite-inside-tag-p tag-info here) | |
250 (setq context (cons tag-info context))) | |
251 | |
252 ;; start-tag | |
253 ((eq (xml-lite-tag-type tag-info) 'open) | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
254 (setq ignore-depth (1- ignore-depth)) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
255 (when (= ignore-depth -1) |
43687 | 256 (setq context (cons tag-info context)) |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
257 (setq ignore-depth 0))) |
43687 | 258 |
259 ;; end-tag | |
260 ((eq (xml-lite-tag-type tag-info) 'close) | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
261 (setq ignore-depth (1+ ignore-depth))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
262 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
263 ((eq (xml-lite-tag-type tag-info) 'comment) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
264 ;; this comment may enclose things we thought were tags |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
265 (while (and context |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
266 (> (xml-lite-tag-end tag-info) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
267 (xml-lite-tag-end (car context)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
268 (setq context (cdr context)))) |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
269 |
43687 | 270 ))) |
271 | |
272 ;; return context | |
273 context | |
274 )) | |
275 | |
276 (defun xml-lite-show-context (&optional full) | |
277 "Display the current context. | |
278 If FULL is non-nil, parse back to the beginning of the buffer." | |
279 (interactive "P") | |
280 (with-output-to-temp-buffer "*XML Context*" | |
281 (pp (xml-lite-get-context full)))) | |
282 | |
283 | |
284 ;; Indenting | |
285 | |
286 (defun xml-lite-calculate-indent () | |
287 "Calculate the column to which this line should be indented." | |
288 (let* ((here (point)) | |
289 (context (xml-lite-get-context)) | |
290 (ref-tag-info (car context)) | |
291 (last-tag-info (car (last context)))) | |
292 | |
293 (save-excursion | |
294 (cond | |
295 | |
296 ;; no context | |
297 ((null context) | |
298 0) | |
299 | |
300 ;; inside a comment | |
301 ((eq 'comment (xml-lite-tag-type last-tag-info)) | |
302 (goto-char (xml-lite-tag-start last-tag-info)) | |
303 (+ (current-column) xml-lite-indent-comment-offset)) | |
304 | |
305 ;; inside a tag | |
306 ((xml-lite-inside-tag-p last-tag-info here) | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
307 |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
308 (let ((in-string |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
309 (xml-lite-in-string-p (xml-lite-tag-start last-tag-info)))) |
43687 | 310 (cond |
311 ;; inside a string | |
44168
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
312 (in-string |
68fd324f9f0f
(xml-lite-at-indentation-p): Move.
Stefan Monnier <monnier@iro.umontreal.ca>
parents:
43687
diff
changeset
|
313 (goto-char (nth 1 in-string)) |
43687 | 314 (1+ (current-column))) |
315 ;; if we have a tag-name, base indent on that | |
316 ((and (xml-lite-tag-name-end last-tag-info) | |
317 (progn | |
318 (goto-char (xml-lite-tag-name-end last-tag-info)) | |
319 (not (looking-at "[ \t]*$")))) | |
320 (1+ (current-column))) | |
321 ;; otherwise, add indent-offset | |
322 (t | |
323 (goto-char (xml-lite-tag-start last-tag-info)) | |
324 (+ (current-column) xml-lite-indent-offset))))) | |
325 | |
326 ;; inside an element | |
327 (t | |
328 ;; indent to start of tag | |
329 (let ((here (point)) | |
330 indent-col) | |
331 (goto-char (xml-lite-tag-start ref-tag-info)) | |
332 (setq indent-col (current-column)) | |
333 (goto-char here) | |
334 ;; add xml-lite-indent-offset, unless we're looking at the matching | |
335 ;; end-tag | |
336 (unless (and (eq (length context) 1) (looking-at "</")) | |
337 (setq indent-col (+ indent-col xml-lite-indent-offset))) | |
338 indent-col)) | |
339 | |
340 )))) | |
341 | |
342 (defun xml-lite-indent-line () | |
343 "Indent the current line as XML." | |
344 (interactive) | |
345 (let ((origin-point (point)) | |
346 bol-point indent-point | |
347 indent-col) | |
348 | |
349 ;; save beginning of line | |
350 (beginning-of-line) | |
351 (setq bol-point (point)) | |
352 ;; save current indent | |
353 (skip-chars-forward " \t") | |
354 (setq indent-point (point)) | |
355 | |
356 ;; calculate basic indent | |
357 (setq indent-col (xml-lite-calculate-indent)) | |
358 | |
359 (unless (eq (current-column) indent-col) | |
360 ;; re-indent, adjusting origin point for indentation change | |
361 (delete-region bol-point (point)) | |
362 (indent-to indent-col) | |
363 (setq origin-point (+ origin-point (- (point) indent-point)))) | |
364 | |
365 (if (> origin-point (point)) | |
366 (goto-char origin-point)) | |
367 | |
368 )) | |
369 | |
370 | |
371 ;; Editing shortcuts | |
372 | |
373 (defun xml-lite-insert-end-tag () | |
374 "Insert an end-tag for the current element." | |
375 (interactive) | |
376 (let* ((context (xml-lite-get-context)) | |
377 (tag-info (car (last context))) | |
378 (type (and tag-info (xml-lite-tag-type tag-info)))) | |
379 | |
380 (cond | |
381 | |
382 ((null context) | |
383 (error "Nothing to close")) | |
384 | |
385 ;; inside a tag | |
386 ((xml-lite-inside-tag-p tag-info) | |
387 (cond | |
388 ((eq type 'open) (insert " />")) | |
389 ((eq type 'comment) (insert " -->")) | |
390 ((eq type 'cdata) (insert "]]>")) | |
391 ((eq type 'jsp) (insert "%>")) | |
392 ((eq type 'pi) (insert "?>")) | |
393 (t (insert ">")))) | |
394 | |
395 ;; inside an element | |
396 ((eq type 'open) | |
397 (insert "</" (xml-lite-tag-name tag-info) ">") | |
398 (xml-lite-indent-line)) | |
399 | |
400 (t | |
401 (error "Nothing to close"))))) | |
402 | |
403 (defun xml-lite-slash (arg) | |
404 "Insert ARG slash characters. | |
405 Behaves electrically if `xml-lite-electric-slash' is non-nil." | |
406 (interactive "p") | |
407 (cond | |
408 ((not (and (eq (char-before) ?<) (= arg 1))) | |
409 (insert-char ?/ arg)) | |
410 ((eq xml-lite-electric-slash 'indent) | |
411 (insert-char ?/ 1) | |
412 (xml-lite-indent-line)) | |
413 ((eq xml-lite-electric-slash 'close) | |
414 (delete-backward-char 1) | |
415 (xml-lite-insert-end-tag)) | |
416 (t | |
417 (insert-char ?/ arg)))) | |
418 | |
419 | |
420 ;; Keymap | |
421 | |
422 (defvar xml-lite-mode-map | |
423 (let ((map (make-sparse-keymap))) | |
424 (define-key map "\t" 'indent-for-tab-command) | |
425 (define-key map "\C-c/" 'xml-lite-insert-end-tag) | |
426 (define-key map "\C-c\C-s" 'xml-lite-show-context) | |
427 (define-key map "/" 'xml-lite-slash) | |
428 map) | |
429 "Key bindings for `xml-lite-mode'.") | |
430 | |
431 | |
432 ;; Minor mode | |
433 | |
434 ;;;###autoload | |
435 (define-minor-mode xml-lite-mode | |
436 "Toggle `xml-lite-mode'. | |
437 With ARG, enable xml-lite-mode if and only if ARG is positive. | |
438 | |
439 xml-lite-mode provides indentation for XML tags. The value of | |
440 `xml-lite-indent-offset' determines the amount of indentation. | |
441 | |
442 Key bindings: | |
443 \\{xml-lite-mode-map}" | |
444 nil ; initial value | |
445 " XML" ; mode indicator | |
446 'xml-lite-mode-map ; keymap | |
447 (if xml-lite-mode | |
448 (progn | |
449 (if (eq major-mode 'fundamental-mode) | |
450 (sgml-mode)) | |
451 (make-local-variable 'indent-line-function) | |
452 (setq xml-lite-mode t | |
453 xml-lite-orig-indent-line-function indent-line-function | |
454 indent-line-function 'xml-lite-indent-line)) | |
455 (setq indent-line-function xml-lite-orig-indent-line-function)) | |
456 (force-mode-line-update)) | |
457 | |
458 (provide 'xml-lite) | |
459 | |
460 ;;; xml-lite.el ends here |