0d75c092fd36c24259eb3a4a5b705a016c63c71c
[bpt/emacs.git] / lisp / nxml / nxml-mode.el
1 ;;; nxml-mode.el --- a new XML mode
2
3 ;; Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
4
5 ;; Author: James Clark
6 ;; Keywords: XML
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; See nxml-rap.el for description of parsing strategy.
26
27 ;;; Code:
28
29 (when (featurep 'mucs)
30 (error "nxml-mode is not compatible with Mule-UCS"))
31
32 (eval-when-compile (require 'cl)) ; for assert
33
34 (require 'xmltok)
35 (require 'nxml-enc)
36 (require 'nxml-glyph)
37 (require 'nxml-util)
38 (require 'nxml-rap)
39 (require 'nxml-outln)
40
41 (declare-function rng-nxml-mode-init "rng-nxml")
42 (declare-function nxml-enable-unicode-char-name-sets "nxml-uchnm")
43
44 ;;; Customization
45
46 (defgroup nxml nil
47 "New XML editing mode."
48 :group 'languages)
49
50 (defgroup nxml-faces nil
51 "Faces for XML syntax highlighting."
52 :group 'nxml)
53
54 (defcustom nxml-char-ref-display-glyph-flag t
55 "*Non-nil means display glyph following character reference.
56 The glyph is displayed in face `nxml-glyph'. The hook
57 `nxml-glyph-set-hook' can be used to customize for which characters
58 glyphs are displayed."
59 :group 'nxml
60 :type 'boolean)
61
62 (defcustom nxml-sexp-element-flag nil
63 "*Non-nil means sexp commands treat an element as a single expression."
64 :group 'nxml
65 :type 'boolean)
66
67 (defcustom nxml-slash-auto-complete-flag nil
68 "*Non-nil means typing a slash automatically completes the end-tag.
69 This is used by `nxml-electric-slash'."
70 :group 'nxml
71 :type 'boolean)
72
73 (defcustom nxml-child-indent 2
74 "*Indentation for the children of an element relative to the start-tag.
75 This only applies when the line or lines containing the start-tag contains
76 nothing else other than that start-tag."
77 :group 'nxml
78 :type 'integer)
79
80 (defcustom nxml-attribute-indent 4
81 "*Indentation for the attributes of an element relative to the start-tag.
82 This only applies when the first attribute of a tag starts a line.
83 In other cases, the first attribute on one line is indented the same
84 as the first attribute on the previous line."
85 :group 'nxml
86 :type 'integer)
87
88 (defcustom nxml-bind-meta-tab-to-complete-flag (not window-system)
89 "*Non-nil means bind M-TAB in `nxml-mode-map' to `nxml-complete'.
90 C-return will be bound to `nxml-complete' in any case.
91 M-TAB gets swallowed by many window systems/managers, and
92 `documentation' will show M-TAB rather than C-return as the
93 binding for `nxml-complete' when both are bound. So it's better
94 to bind M-TAB only when it will work."
95 :group 'nxml
96 :set (lambda (sym flag)
97 (set-default sym flag)
98 (when (and (boundp 'nxml-mode-map) nxml-mode-map)
99 (define-key nxml-mode-map "\M-\t" (and flag 'nxml-complete))))
100 :type 'boolean)
101
102 (defcustom nxml-prefer-utf-16-to-utf-8-flag nil
103 "*Non-nil means prefer UTF-16 to UTF-8 when saving a buffer.
104 This is used only when a buffer does not contain an encoding declaration
105 and when its current `buffer-file-coding-system' specifies neither UTF-16
106 nor UTF-8."
107 :group 'nxml
108 :type 'boolean)
109
110 (defcustom nxml-prefer-utf-16-little-to-big-endian-flag (eq system-type
111 'windows-nt)
112 "*Non-nil means prefer little-endian to big-endian byte-order for UTF-16.
113 This is used only for saving a buffer; when reading the byte-order is
114 auto-detected. It may be relevant both when there is no encoding declaration
115 and when the encoding declaration specifies `UTF-16'."
116 :group 'nxml
117 :type 'boolean)
118
119 (defcustom nxml-default-buffer-file-coding-system nil
120 "*Default value for `buffer-file-coding-system' for a buffer for a new file.
121 A value of nil means use the default value of `buffer-file-coding-system' as normal.
122 A buffer's `buffer-file-coding-system' affects what \\[nxml-insert-xml-declaration] inserts."
123 :group 'nxml
124 :type 'coding-system)
125
126 (defcustom nxml-auto-insert-xml-declaration-flag nil
127 "*Non-nil means automatically insert an XML declaration in a new file.
128 The XML declaration is inserted using `nxml-insert-xml-declaration'."
129 :group 'nxml
130 :type 'boolean)
131
132 (defface nxml-delimited-data
133 '((t (:inherit font-lock-doc-face)))
134 "Face used to highlight data enclosed between delimiters.
135 This is not used directly, but only via inheritance by other faces."
136 :group 'nxml-faces)
137
138 (defface nxml-name
139 '((t (:inherit font-lock-builtin-face)))
140 "Face used to highlight various names.
141 This includes element and attribute names, processing
142 instruction targets and the CDATA keyword in a CDATA section.
143 This is not used directly, but only via inheritance by other faces."
144 :group 'nxml-faces)
145
146 (defface nxml-ref
147 '((t (:inherit font-lock-constant-face)))
148 "Face used to highlight character and entity references.
149 This is not used directly, but only via inheritance by other faces."
150 :group 'nxml-faces)
151
152 (defface nxml-delimiter
153 nil
154 "Face used to highlight delimiters.
155 This is not used directly, but only via inheritance by other faces."
156 :group 'nxml-faces)
157
158 (defface nxml-text
159 nil
160 "Face used to highlight text."
161 :group 'nxml-faces)
162
163 (defface nxml-comment-content
164 '((t (:inherit font-lock-comment-face)))
165 "Face used to highlight the content of comments."
166 :group 'nxml-faces)
167
168 (defface nxml-comment-delimiter
169 '((t (:inherit font-lock-comment-delimiter-face)))
170 "Face used for the delimiters of comments, i.e <!-- and -->."
171 :group 'nxml-faces)
172
173 (defface nxml-processing-instruction-delimiter
174 '((t (:inherit nxml-delimiter)))
175 "Face used for the delimiters of processing instructions, i.e <? and ?>."
176 :group 'nxml-faces)
177
178 (defface nxml-processing-instruction-target
179 '((t (:inherit font-lock-keyword-face)))
180 "Face used for the target of processing instructions."
181 :group 'nxml-faces)
182
183 (defface nxml-processing-instruction-content
184 '((t (:inherit nxml-delimited-data)))
185 "Face used for the content of processing instructions."
186 :group 'nxml-faces)
187
188 (defface nxml-cdata-section-delimiter
189 '((t (:inherit nxml-delimiter)))
190 "Face used for the delimiters of CDATA sections, i.e <![, [, and ]]>."
191 :group 'nxml-faces)
192
193 (defface nxml-cdata-section-CDATA
194 '((t (:inherit nxml-name)))
195 "Face used for the CDATA keyword in CDATA sections."
196 :group 'nxml-faces)
197
198 (defface nxml-cdata-section-content
199 '((t (:inherit nxml-text)))
200 "Face used for the content of CDATA sections."
201 :group 'nxml-faces)
202
203 (defface nxml-char-ref-number
204 '((t (:inherit nxml-ref)))
205 "Face used for the number in character references.
206 This includes ths `x' in hex references."
207 :group 'nxml-faces)
208
209 (defface nxml-char-ref-delimiter
210 '((t (:inherit nxml-ref)))
211 "Face used for the delimiters of character references, i.e &# and ;."
212 :group 'nxml-faces)
213
214 (defface nxml-entity-ref-name
215 '((t (:inherit nxml-ref)))
216 "Face used for the entity name in general entity references."
217 :group 'nxml-faces)
218
219 (defface nxml-entity-ref-delimiter
220 '((t (:inherit nxml-ref)))
221 "Face used for the delimiters of entity references, i.e & and ;."
222 :group 'nxml-faces)
223
224 (defface nxml-tag-delimiter
225 '((t (:inherit nxml-delimiter)))
226 "Face used for the angle brackets delimiting tags.
227 `nxml-tag-slash' is used for slashes."
228 :group 'nxml-faces)
229
230 (defface nxml-tag-slash
231 '((t (:inherit nxml-tag-delimiter)))
232 "Face used for slashes in tags, both in end-tags and empty-elements."
233 :group 'nxml-faces)
234
235 (defface nxml-element-prefix
236 '((t (:inherit nxml-name)))
237 "Face used for the prefix of elements."
238 :group 'nxml-faces)
239
240 (defface nxml-element-colon
241 nil
242 "Face used for the colon in element names."
243 :group 'nxml-faces)
244
245 (defface nxml-element-local-name
246 '((t (:inherit font-lock-function-name-face)))
247 "Face used for the local name of elements."
248 :group 'nxml-faces)
249
250 (defface nxml-attribute-prefix
251 '((t (:inherit nxml-name)))
252 "Face used for the prefix of attributes."
253 :group 'nxml-faces)
254
255 (defface nxml-attribute-colon
256 '((t (:inherit nxml-delimiter)))
257 "Face used for the colon in attribute names."
258 :group 'nxml-faces)
259
260 (defface nxml-attribute-local-name
261 '((t (:inherit font-lock-variable-name-face)))
262 "Face used for the local name of attributes."
263 :group 'nxml-faces)
264
265 (defface nxml-namespace-attribute-xmlns
266 '((t (:inherit nxml-attribute-prefix)))
267 "Face used for `xmlns' in namespace attributes."
268 :group 'nxml-faces)
269
270 (defface nxml-namespace-attribute-colon
271 '((t (:inherit nxml-attribute-colon)))
272 "Face used for the colon in namespace attributes."
273 :group 'nxml-faces)
274
275 (defface nxml-namespace-attribute-prefix
276 '((t (:inherit nxml-attribute-local-name)))
277 "Face used for the prefix declared in namespace attributes."
278 :group 'nxml-faces)
279
280 (defface nxml-attribute-value
281 '((t (:inherit font-lock-string-face)))
282 "Face used for the value of attributes."
283 :group 'nxml-faces)
284
285 (defface nxml-attribute-value-delimiter
286 '((t (:inherit nxml-attribute-value)))
287 "Face used for the delimiters of attribute values."
288 :group 'nxml-faces)
289
290 (defface nxml-namespace-attribute-value
291 '((t (:inherit nxml-attribute-value)))
292 "Face used for the value of namespace attributes."
293 :group 'nxml-faces)
294
295 (defface nxml-namespace-attribute-value-delimiter
296 '((t (:inherit nxml-attribute-value-delimiter)))
297 "Face used for the delimiters of namespace attribute values."
298 :group 'nxml-faces)
299
300 (defface nxml-prolog-literal-delimiter
301 '((t (:inherit nxml-delimited-data)))
302 "Face used for the delimiters of literals in the prolog."
303 :group 'nxml-faces)
304
305 (defface nxml-prolog-literal-content
306 '((t (:inherit nxml-delimited-data)))
307 "Face used for the content of literals in the prolog."
308 :group 'nxml-faces)
309
310 (defface nxml-prolog-keyword
311 '((t (:inherit font-lock-keyword-face)))
312 "Face used for keywords in the prolog."
313 :group 'nxml-faces)
314
315 (defface nxml-markup-declaration-delimiter
316 '((t (:inherit nxml-delimiter)))
317 "Face used for the delimiters of markup declarations in the prolog.
318 The delimiters are <! and >."
319 :group 'nxml-faces)
320
321 (defface nxml-hash
322 '((t (:inherit nxml-name)))
323 "Face used for # before a name in the prolog."
324 :group 'nxml-faces)
325
326 (defface nxml-glyph
327 '((((type x))
328 (:family
329 "misc-fixed"
330 :background
331 "light grey"
332 :foreground
333 "black"
334 :weight
335 normal
336 :slant
337 normal))
338 (t
339 (:background
340 "light grey"
341 :foreground
342 "black"
343 :weight
344 normal
345 :slant
346 normal)))
347 "Face used for glyph for char references."
348 :group 'nxml-faces)
349
350 ;;; Global variables
351
352 (defvar nxml-parent-document nil
353 "The parent document for a part of a modular document.
354 Use `nxml-parent-document-set' to set it.")
355 (make-variable-buffer-local 'nxml-parent-document)
356 (put 'nxml-parent-document 'safe-local-variable 'stringp)
357
358 (defvar nxml-prolog-regions nil
359 "List of regions in the prolog to be fontified.
360 See the function `xmltok-forward-prolog' for more information.")
361 (make-variable-buffer-local 'nxml-prolog-regions)
362
363 (defvar nxml-last-fontify-end nil
364 "Position where fontification last ended.
365 It is nil if the buffer changed since the last fontification.")
366 (make-variable-buffer-local 'nxml-last-fontify-end)
367
368 (defvar nxml-degraded nil
369 "Non-nil if currently operating in degraded mode.
370 Degraded mode is enabled when an internal error is encountered in the
371 fontification or after-change functions.")
372 (make-variable-buffer-local 'nxml-degraded)
373
374 (defvar nxml-completion-hook nil
375 "Hook run by `nxml-complete'.
376 This hook is run until success.")
377
378 (defvar nxml-in-mixed-content-hook nil
379 "Hook to determine whether point is in mixed content.
380 The hook is called without arguments. It should return nil if it is
381 definitely not mixed; non-nil otherwise. The hook will be run until
382 one of the functions returns nil.")
383
384 (defvar nxml-mixed-scan-distance 4000
385 "Maximum distance from point to scan when checking for mixed content.")
386
387 (defvar nxml-end-tag-indent-scan-distance 4000
388 "Maximum distance from point to scan backwards when indenting end-tag.")
389
390 (defvar nxml-char-ref-extra-display t
391 "Non-nil means display extra information for character references.
392 The extra information consists of a tooltip with the character name
393 and, if `nxml-char-ref-display-glyph-flag' is non-nil, a glyph
394 corresponding to the referenced character following the character
395 reference.")
396 (make-variable-buffer-local 'nxml-char-ref-extra-display)
397
398 (defvar nxml-mode-map
399 (let ((map (make-sparse-keymap)))
400 (define-key map "\M-\C-u" 'nxml-backward-up-element)
401 (define-key map "\M-\C-d" 'nxml-down-element)
402 (define-key map "\M-\C-n" 'nxml-forward-element)
403 (define-key map "\M-\C-p" 'nxml-backward-element)
404 (define-key map "\M-{" 'nxml-backward-paragraph)
405 (define-key map "\M-}" 'nxml-forward-paragraph)
406 (define-key map "\M-h" 'nxml-mark-paragraph)
407 (define-key map "\C-c\C-f" 'nxml-finish-element)
408 (define-key map "\C-c]" 'nxml-finish-element)
409 (define-key map "\C-c/" 'nxml-finish-element)
410 (define-key map "\C-c\C-m" 'nxml-split-element)
411 (define-key map "\C-c\C-b" 'nxml-balanced-close-start-tag-block)
412 (define-key map "\C-c\C-i" 'nxml-balanced-close-start-tag-inline)
413 (define-key map "\C-c\C-x" 'nxml-insert-xml-declaration)
414 (define-key map "\C-c\C-d" 'nxml-dynamic-markup-word)
415 ;; u is for Unicode
416 (define-key map "\C-c\C-u" 'nxml-insert-named-char)
417 (define-key map "\C-c\C-o" nxml-outline-prefix-map)
418 (define-key map [S-mouse-2] 'nxml-mouse-hide-direct-text-content)
419 (define-key map "/" 'nxml-electric-slash)
420 (define-key map [C-return] 'nxml-complete)
421 (when nxml-bind-meta-tab-to-complete-flag
422 (define-key map "\M-\t" 'nxml-complete))
423 map)
424 "Keymap for nxml-mode.")
425
426 (defvar nxml-font-lock-keywords
427 '(nxml-fontify-matcher)
428 "Default font lock keywords for nxml-mode.")
429
430 (defsubst nxml-set-face (start end face)
431 (when (and face (< start end))
432 (font-lock-append-text-property start end 'face face)))
433
434 (defun nxml-parent-document-set (parent-document)
435 "Set `nxml-parent-document' and inherit the DTD &c."
436 ;; FIXME: this does not work.
437 ;; the idea is that by inheriting some variables from the parent,
438 ;; `rng-validate-mode' will validate entities declared in the parent.
439 ;; alas, the most interesting variables (`rng-compile-table' et al)
440 ;; are circular and cannot be printed even with `print-circle'.
441 (interactive "fParent document")
442 (let (dtd current-schema current-schema-file-name compile-table
443 ipattern-table last-ipattern-index)
444 (when (string= (file-truename parent-document)
445 (file-truename buffer-file-name))
446 (error "Parent document cannot be the same as the document"))
447 (with-current-buffer (find-file-noselect parent-document)
448 (setq dtd rng-dtd
449 current-schema rng-current-schema
450 current-schema-file-name rng-current-schema-file-name
451 compile-table rng-compile-table
452 ipattern-table rng-ipattern-table
453 last-ipattern-index rng-last-ipattern-index
454 parent-document buffer-file-name))
455 (setq rng-dtd dtd
456 rng-current-schema current-schema
457 rng-current-schema-file-name current-schema-file-name
458 rng-compile-table compile-table
459 rng-ipattern-table ipattern-table
460 rng-last-ipattern-index last-ipattern-index
461 nxml-parent-document parent-document)
462 (message "Set parent document to %s" parent-document)
463 (when rng-validate-mode
464 (rng-validate-while-idle (current-buffer)))))
465
466 ;;;###autoload
467 (define-derived-mode nxml-mode text-mode "nXML"
468 ;; We use C-c C-i instead of \\[nxml-balanced-close-start-tag-inline]
469 ;; because Emacs turns C-c C-i into C-c TAB which is hard to type and
470 ;; not mnemonic.
471 "Major mode for editing XML.
472
473 \\[nxml-finish-element] finishes the current element by inserting an end-tag.
474 C-c C-i closes a start-tag with `>' and then inserts a balancing end-tag
475 leaving point between the start-tag and end-tag.
476 \\[nxml-balanced-close-start-tag-block] is similar but for block rather than inline elements:
477 the start-tag, point, and end-tag are all left on separate lines.
478 If `nxml-slash-auto-complete-flag' is non-nil, then inserting a `</'
479 automatically inserts the rest of the end-tag.
480
481 \\[nxml-complete] performs completion on the symbol preceding point.
482
483 \\[nxml-dynamic-markup-word] uses the contents of the current buffer
484 to choose a tag to put around the word preceding point.
485
486 Sections of the document can be displayed in outline form. The
487 variable `nxml-section-element-name-regexp' controls when an element
488 is recognized as a section. The same key sequences that change
489 visibility in outline mode are used except that they start with C-c C-o
490 instead of C-c.
491
492 Validation is provided by the related minor-mode `rng-validate-mode'.
493 This also makes completion schema- and context- sensitive. Element
494 names, attribute names, attribute values and namespace URIs can all be
495 completed. By default, `rng-validate-mode' is automatically enabled.
496 You can toggle it using \\[rng-validate-mode] or change the default by
497 customizing `rng-nxml-auto-validate-flag'.
498
499 \\[indent-for-tab-command] indents the current line appropriately.
500 This can be customized using the variable `nxml-child-indent'
501 and the variable `nxml-attribute-indent'.
502
503 \\[nxml-insert-named-char] inserts a character reference using
504 the character's name (by default, the Unicode name).
505 \\[universal-argument] \\[nxml-insert-named-char] inserts the character directly.
506
507 The Emacs commands that normally operate on balanced expressions will
508 operate on XML markup items. Thus \\[forward-sexp] will move forward
509 across one markup item; \\[backward-sexp] will move backward across
510 one markup item; \\[kill-sexp] will kill the following markup item;
511 \\[mark-sexp] will mark the following markup item. By default, each
512 tag each treated as a single markup item; to make the complete element
513 be treated as a single markup item, set the variable
514 `nxml-sexp-element-flag' to t. For more details, see the function
515 `nxml-forward-balanced-item'.
516
517 \\[nxml-backward-up-element] and \\[nxml-down-element] move up and down the element structure.
518
519 Many aspects this mode can be customized using
520 \\[customize-group] nxml RET."
521 ;; (kill-all-local-variables)
522 (set (make-local-variable 'mode-line-process) '((nxml-degraded "/degraded")))
523 ;; We'll determine the fill prefix ourselves
524 (make-local-variable 'adaptive-fill-mode)
525 (setq adaptive-fill-mode nil)
526 (make-local-variable 'forward-sexp-function)
527 (setq forward-sexp-function 'nxml-forward-balanced-item)
528 (make-local-variable 'indent-line-function)
529 (setq indent-line-function 'nxml-indent-line)
530 (make-local-variable 'fill-paragraph-function)
531 (setq fill-paragraph-function 'nxml-do-fill-paragraph)
532 ;; Comment support
533 ;; This doesn't seem to work too well;
534 ;; I think we should probably roll our own nxml-comment-dwim function.
535 (make-local-variable 'comment-indent-function)
536 (setq comment-indent-function 'nxml-indent-line)
537 (make-local-variable 'comment-start)
538 (setq comment-start "<!--")
539 (make-local-variable 'comment-start-skip)
540 (setq comment-start-skip "<!--[ \t\r\n]*")
541 (make-local-variable 'comment-end)
542 (setq comment-end "-->")
543 (make-local-variable 'comment-end-skip)
544 (setq comment-end-skip "[ \t\r\n]*-->")
545 (make-local-variable 'comment-line-break-function)
546 (setq comment-line-break-function 'nxml-newline-and-indent)
547 (use-local-map nxml-mode-map)
548 (save-excursion
549 (save-restriction
550 (widen)
551 (nxml-clear-dependent-regions (point-min) (point-max))
552 (setq nxml-scan-end (copy-marker (point-min) nil))
553 (nxml-with-unmodifying-text-property-changes
554 (nxml-clear-inside (point-min) (point-max))
555 (nxml-with-invisible-motion
556 (nxml-scan-prolog)))))
557 (add-hook 'after-change-functions 'nxml-after-change nil t)
558 (add-hook 'change-major-mode-hook 'nxml-cleanup nil t)
559
560 ;; Emacs 23 handles the encoding attribute on the xml declaration
561 ;; transparently to nxml-mode, so there is no longer a need for the below
562 ;; hook. The hook also had the drawback of overriding explicit user
563 ;; instruction to save as some encoding other than utf-8.
564 ;;; (add-hook 'write-contents-hooks 'nxml-prepare-to-save)
565 (when (not (and (buffer-file-name) (file-exists-p (buffer-file-name))))
566 (when (and nxml-default-buffer-file-coding-system
567 (not (local-variable-p 'buffer-file-coding-system)))
568 (setq buffer-file-coding-system nxml-default-buffer-file-coding-system))
569 (when nxml-auto-insert-xml-declaration-flag
570 (nxml-insert-xml-declaration)))
571
572 (setq font-lock-defaults
573 '(nxml-font-lock-keywords
574 t ; keywords-only; we highlight comments and strings here
575 nil ; font-lock-keywords-case-fold-search. XML is case sensitive
576 nil ; no special syntax table
577 nil ; no automatic syntactic fontification
578 (font-lock-extend-after-change-region-function
579 . nxml-extend-after-change-region)
580 (font-lock-extend-region-functions . (nxml-extend-region))
581 (jit-lock-contextually . t)
582 (font-lock-unfontify-region-function . nxml-unfontify-region)))
583
584 (rng-nxml-mode-init)
585 (nxml-enable-unicode-char-name-sets))
586
587 (defun nxml-cleanup ()
588 "Clean up after nxml-mode."
589 ;; Disable associated minor modes.
590 (rng-validate-mode -1)
591 ;; Clean up fontification.
592 (save-excursion
593 (widen)
594 (let ((inhibit-read-only t)
595 (buffer-undo-list t)
596 (modified (buffer-modified-p)))
597 (nxml-with-invisible-motion
598 (remove-text-properties (point-min) (point-max) '(face)))
599 (set-buffer-modified-p modified)))
600 (remove-hook 'change-major-mode-hook 'nxml-cleanup t))
601
602 (defun nxml-degrade (context err)
603 (message "Internal nXML mode error in %s (%s), degrading"
604 context
605 (error-message-string err))
606 (ding)
607 (setq nxml-degraded t)
608 (setq nxml-prolog-end 1)
609 (save-excursion
610 (save-restriction
611 (widen)
612 (nxml-with-unmodifying-text-property-changes
613 (nxml-clear-inside (point-min) (point-max))))))
614
615 ;;; Change management
616
617 (defun nxml-debug-region (start end)
618 (interactive "r")
619 (let ((font-lock-beg start)
620 (font-lock-end end))
621 (nxml-extend-region)
622 (goto-char font-lock-beg)
623 (set-mark font-lock-end)))
624
625 (defun nxml-after-change (start end pre-change-length)
626 ; In font-lock mode, nxml-after-change1 is called via
627 ; nxml-extend-after-change-region instead so that the updated
628 ; book-keeping information is available for fontification.
629 (unless (or font-lock-mode nxml-degraded)
630 (nxml-with-degradation-on-error 'nxml-after-change
631 (save-excursion
632 (save-restriction
633 (widen)
634 (save-match-data
635 (nxml-with-invisible-motion
636 (nxml-with-unmodifying-text-property-changes
637 (nxml-after-change1
638 start end pre-change-length)))))))))
639
640 (defun nxml-after-change1 (start end pre-change-length)
641 "After-change bookkeeping.
642 Returns a cons cell containing a possibly-enlarged change region.
643 You must call `nxml-extend-region' on this expanded region to obtain
644 the full extent of the area needing refontification.
645
646 For bookkeeping, call this function even when fontification is
647 disabled."
648 (let ((pre-change-end (+ start pre-change-length)))
649 (setq start
650 (nxml-adjust-start-for-dependent-regions start
651 end
652 pre-change-length))
653 ;; If the prolog might have changed, rescan the prolog
654 (when (<= start
655 ;; Add 2 so as to include the < and following char that
656 ;; start the instance (document element), since changing
657 ;; these can change where the prolog ends.
658 (+ nxml-prolog-end 2))
659 ;; end must be extended to at least the end of the old prolog in
660 ;; case the new prolog is shorter
661 (when (< pre-change-end nxml-prolog-end)
662 (setq end
663 ;; don't let end get out of range even if pre-change-length
664 ;; is bogus
665 (min (point-max)
666 (+ end (- nxml-prolog-end pre-change-end)))))
667 (nxml-scan-prolog)
668 (setq start (point-min))))
669
670 (when (> end nxml-prolog-end)
671 (goto-char start)
672 (nxml-move-tag-backwards (point-min))
673 (setq start (point))
674 (setq end (max (nxml-scan-after-change start end)
675 end)))
676
677 (nxml-debug-change "nxml-after-change1" start end)
678 (cons start end))
679
680 ;;; Encodings
681
682 (defun nxml-insert-xml-declaration ()
683 "Insert an XML declaration at the beginning of buffer.
684 The XML declaration will declare an encoding depending on the buffer's
685 `buffer-file-coding-system'."
686 (interactive "*")
687 (let ((coding-system
688 (if (and buffer-file-coding-system
689 (coding-system-p buffer-file-coding-system)
690 (coding-system-get buffer-file-coding-system
691 'mime-charset))
692 buffer-file-coding-system
693 (nxml-choose-utf-coding-system))))
694 (goto-char (point-min))
695 (insert (format "<?xml version=\"1.0\" encoding=\"%s\"?>\n"
696 (nxml-coding-system-name coding-system)))))
697
698 (defun nxml-prepare-to-save ()
699 (unless (and (not enable-multibyte-characters)
700 (local-variable-p 'buffer-file-coding-system)
701 buffer-file-coding-system
702 (or (eq (coding-system-type buffer-file-coding-system) 5)
703 (eq buffer-file-coding-system 'no-conversion)))
704 (save-excursion
705 (setq buffer-file-coding-system (nxml-select-coding-system))))
706 ;; nil from a function in `write-contents-hooks' means
707 ;; to continue and write the file as normal
708 nil)
709
710 (defun nxml-select-coding-system ()
711 (let* ((suitable-coding-systems
712 (find-coding-systems-region (point-min) (point-max)))
713 (enc-pos (progn
714 (goto-char (point-min))
715 (xmltok-get-declared-encoding-position)))
716 (enc-name
717 (and (consp enc-pos)
718 (buffer-substring-no-properties (car enc-pos)
719 (cdr enc-pos))))
720 (coding-system
721 (cond (enc-name
722 (if (string= (downcase enc-name) "utf-16")
723 (nxml-choose-utf-16-coding-system)
724 (nxml-mime-charset-coding-system enc-name)))
725 (enc-pos (nxml-choose-utf-coding-system)))))
726 ;; Make sure we have a coding-system
727 (unless coding-system
728 (setq coding-system
729 (and (not buffer-read-only)
730 (nxml-choose-suitable-coding-system
731 suitable-coding-systems)))
732 (let ((message
733 (if enc-name
734 (format "Unknown encoding %s" enc-name)
735 "XML declaration is not well-formed")))
736 (cond ((not coding-system)
737 (error "%s" message))
738 ((y-or-n-p
739 (concat message
740 ". "
741 (format (if enc-name
742 "Save with %s"
743 "Modify and save with encoding %s")
744 (nxml-coding-system-name coding-system))
745 " "))
746 (nxml-fix-encoding-declaration enc-pos coding-system))
747 (t (signal 'quit nil)))))
748 ;; Make sure it can encode all the characters in the buffer
749 (unless (or (memq (coding-system-base coding-system)
750 suitable-coding-systems)
751 (equal suitable-coding-systems '(undecided)))
752 (let ((message
753 (nxml-unsuitable-coding-system-message coding-system
754 enc-name)))
755 (setq coding-system
756 (and (not buffer-read-only)
757 (nxml-choose-suitable-coding-system
758 suitable-coding-systems)))
759 (cond ((not coding-system) (error "%s" message))
760 ((y-or-n-p (concat message
761 (format ". Save with %s "
762 (nxml-coding-system-name
763 coding-system))))
764 (nxml-fix-encoding-declaration enc-pos coding-system))
765 (t (signal 'quit nil)))))
766 ;; Merge the newline type of our existing encoding
767 (let ((current-eol-type
768 (coding-system-eol-type buffer-file-coding-system)))
769 (when (and current-eol-type (integerp current-eol-type))
770 (setq coding-system
771 (coding-system-change-eol-conversion coding-system
772 current-eol-type))))
773 coding-system))
774
775 (defun nxml-unsuitable-coding-system-message (coding-system &optional enc-name)
776 (if (nxml-coding-system-unicode-p coding-system)
777 "Cannot translate some characters to Unicode"
778 (format "Cannot encode some characters with %s"
779 (or enc-name
780 (nxml-coding-system-name coding-system)))))
781
782 (defconst nxml-utf-16-coding-systems (and (coding-system-p 'utf-16-be)
783 (coding-system-p 'utf-16-le)
784 '(utf-16-be utf-16-le)))
785
786 (defconst nxml-utf-coding-systems (cons 'utf-8 nxml-utf-16-coding-systems))
787
788 (defun nxml-coding-system-unicode-p (coding-system)
789 (nxml-coding-system-member (coding-system-base coding-system)
790 nxml-utf-coding-systems))
791
792 (defun nxml-coding-system-name (coding-system)
793 (setq coding-system (coding-system-base coding-system))
794 (symbol-name
795 (if (nxml-coding-system-member coding-system nxml-utf-16-coding-systems)
796 'utf-16
797 (or (coding-system-get coding-system 'mime-charset)
798 coding-system))))
799
800 (defun nxml-fix-encoding-declaration (enc-pos coding-system)
801 (let ((charset (nxml-coding-system-name coding-system)))
802 (cond ((consp enc-pos)
803 (delete-region (car enc-pos) (cdr enc-pos))
804 (goto-char (car enc-pos))
805 (insert charset))
806 ((integerp enc-pos)
807 (goto-char enc-pos)
808 (insert " encoding=\"" charset ?\"))
809 (t
810 (goto-char (point-min))
811 (insert "<?xml version=\"1.0\" encoding=\""
812 charset
813 "\"?>\n")
814 (when (and (not enc-pos)
815 (let ((case-fold-search t))
816 (looking-at xmltok-bad-xml-decl-regexp)))
817 (delete-region (point) (match-end 0)))))))
818
819 (defun nxml-choose-suitable-coding-system (suitable-coding-systems)
820 (let (ret coding-system)
821 (if (and buffer-file-coding-system
822 (memq (coding-system-base buffer-file-coding-system)
823 suitable-coding-systems))
824 buffer-file-coding-system
825 (while (and suitable-coding-systems (not ret))
826 (setq coding-system (car suitable-coding-systems))
827 (if (coding-system-get coding-system 'mime-charset)
828 (setq ret coding-system)
829 (setq suitable-coding-systems (cdr suitable-coding-systems))))
830 ret)))
831
832 (defun nxml-choose-utf-coding-system ()
833 (let ((cur (and (local-variable-p 'buffer-file-coding-system)
834 buffer-file-coding-system
835 (coding-system-base buffer-file-coding-system))))
836 (cond ((car (nxml-coding-system-member cur nxml-utf-coding-systems)))
837 ((and nxml-prefer-utf-16-to-utf-8-flag
838 (coding-system-p 'utf-16-le)
839 (coding-system-p 'utf-16-be))
840 (if nxml-prefer-utf-16-little-to-big-endian-flag
841 'utf-16-le
842 'utf-16-be))
843 (t 'utf-8))))
844
845 (defun nxml-choose-utf-16-coding-system ()
846 (let ((cur (and (local-variable-p 'buffer-file-coding-system)
847 buffer-file-coding-system
848 (coding-system-base buffer-file-coding-system))))
849 (cond ((car (nxml-coding-system-member cur nxml-utf-16-coding-systems)))
850 (nxml-prefer-utf-16-little-to-big-endian-flag
851 (and (coding-system-p 'utf-16-le) 'utf-16-le))
852 (t (and (coding-system-p 'utf-16-be) 'utf-16-be)))))
853
854 (defun nxml-coding-system-member (coding-system coding-systems)
855 (let (ret)
856 (while (and coding-systems (not ret))
857 (if (coding-system-equal coding-system
858 (car coding-systems))
859 (setq ret coding-systems)
860 (setq coding-systems (cdr coding-systems))))
861 ret))
862
863 ;;; Fontification
864
865 (defun nxml-unfontify-region (start end)
866 (font-lock-default-unfontify-region start end)
867 (nxml-clear-char-ref-extra-display start end))
868
869 (defvar font-lock-beg) (defvar font-lock-end)
870 (defun nxml-extend-region ()
871 "Extend the region to hold the minimum area we can fontify with nXML.
872 Called with `font-lock-beg' and `font-lock-end' dynamically bound."
873 (let ((start font-lock-beg)
874 (end font-lock-end))
875
876 (nxml-debug-change "nxml-extend-region(input)" start end)
877
878 (when (< start nxml-prolog-end)
879 (setq start (point-min)))
880
881 (cond ((<= end nxml-prolog-end)
882 (setq end nxml-prolog-end))
883
884 (t
885 (goto-char start)
886 ;; some font-lock backends (like Emacs 22 jit-lock) snap
887 ;; the region to the beginning of the line no matter what
888 ;; we say here. To mitigate the resulting excess
889 ;; fontification, ignore leading whitespace.
890 (skip-syntax-forward " ")
891
892 ;; find the beginning of the previous tag
893 (when (not (equal (char-after) ?\<))
894 (search-backward "<" nxml-prolog-end t))
895 (nxml-ensure-scan-up-to-date)
896 (nxml-move-outside-backwards)
897 (setq start (point))
898
899 (while (< (point) end)
900 (nxml-tokenize-forward))
901
902 (setq end (point))))
903
904 (when (or (< start font-lock-beg)
905 (> end font-lock-end))
906 (setq font-lock-beg start
907 font-lock-end end)
908 (nxml-debug-change "nxml-extend-region" start end)
909 t)))
910
911 (defun nxml-extend-after-change-region (start end pre-change-length)
912 (unless nxml-degraded
913 (setq nxml-last-fontify-end nil)
914 (let ((region (nxml-with-degradation-on-error
915 'nxml-extend-after-change-region
916 (save-excursion
917 (save-restriction
918 (widen)
919 (save-match-data
920 (nxml-with-invisible-motion
921 (nxml-with-unmodifying-text-property-changes
922 (nxml-extend-after-change-region1
923 start end pre-change-length)))))))))
924 (if (consp region) region))))
925
926 (defun nxml-extend-after-change-region1 (start end pre-change-length)
927 (let* ((region (nxml-after-change1 start end pre-change-length))
928 (font-lock-beg (car region))
929 (font-lock-end (cdr region)))
930
931 (nxml-extend-region)
932 (cons font-lock-beg font-lock-end)))
933
934 (defun nxml-fontify-matcher (bound)
935 "Called as font-lock keyword matcher."
936
937 (unless nxml-degraded
938 (nxml-debug-change "nxml-fontify-matcher" (point) bound)
939
940 (when (< (point) nxml-prolog-end)
941 ;; prolog needs to be fontified in one go, and
942 ;; nxml-extend-region makes sure we start at BOB.
943 (assert (bobp))
944 (nxml-fontify-prolog)
945 (goto-char nxml-prolog-end))
946
947 (let (xmltok-dependent-regions
948 xmltok-errors)
949 (while (and (nxml-tokenize-forward)
950 (<= (point) bound)) ; intervals are open-ended
951 (nxml-apply-fontify-rule)))
952
953 (setq nxml-last-fontify-end (point)))
954
955 ;; Since we did the fontification internally, tell font-lock to not
956 ;; do anything itself.
957 nil)
958
959 (defun nxml-fontify-prolog ()
960 "Fontify the prolog.
961 The buffer is assumed to be prepared for fontification.
962 This does not set the fontified property, but it does clear
963 faces appropriately."
964 (let ((regions nxml-prolog-regions))
965 (while regions
966 (let ((region (car regions)))
967 (nxml-apply-fontify-rule (aref region 0)
968 (aref region 1)
969 (aref region 2)))
970 (setq regions (cdr regions)))))
971
972 ;; Vectors identify a substring of the token to be highlighted in some face.
973
974 ;; Token types returned by xmltok-forward.
975
976 (put 'start-tag
977 'nxml-fontify-rule
978 '([nil 1 nxml-tag-delimiter]
979 [-1 nil nxml-tag-delimiter]
980 (element-qname . 1)
981 attributes))
982
983 (put 'partial-start-tag
984 'nxml-fontify-rule
985 '([nil 1 nxml-tag-delimiter]
986 (element-qname . 1)
987 attributes))
988
989 (put 'end-tag
990 'nxml-fontify-rule
991 '([nil 1 nxml-tag-delimiter]
992 [1 2 nxml-tag-slash]
993 [-1 nil nxml-tag-delimiter]
994 (element-qname . 2)))
995
996 (put 'partial-end-tag
997 'nxml-fontify-rule
998 '([nil 1 nxml-tag-delimiter]
999 [1 2 nxml-tag-slash]
1000 (element-qname . 2)))
1001
1002 (put 'empty-element
1003 'nxml-fontify-rule
1004 '([nil 1 nxml-tag-delimiter]
1005 [-2 -1 nxml-tag-slash]
1006 [-1 nil nxml-tag-delimiter]
1007 (element-qname . 1)
1008 attributes))
1009
1010 (put 'partial-empty-element
1011 'nxml-fontify-rule
1012 '([nil 1 nxml-tag-delimiter]
1013 [-1 nil nxml-tag-slash]
1014 (element-qname . 1)
1015 attributes))
1016
1017 (put 'char-ref
1018 'nxml-fontify-rule
1019 '([nil 2 nxml-char-ref-delimiter]
1020 [2 -1 nxml-char-ref-number]
1021 [-1 nil nxml-char-ref-delimiter]
1022 char-ref))
1023
1024 (put 'entity-ref
1025 'nxml-fontify-rule
1026 '([nil 1 nxml-entity-ref-delimiter]
1027 [1 -1 nxml-entity-ref-name]
1028 [-1 nil nxml-entity-ref-delimiter]))
1029
1030 (put 'comment
1031 'nxml-fontify-rule
1032 '([nil 4 nxml-comment-delimiter]
1033 [4 -3 nxml-comment-content]
1034 [-3 nil nxml-comment-delimiter]))
1035
1036 (put 'processing-instruction
1037 'nxml-fontify-rule
1038 '([nil 2 nxml-processing-instruction-delimiter]
1039 [-2 nil nxml-processing-instruction-delimiter]
1040 processing-instruction-content))
1041
1042 (put 'cdata-section
1043 'nxml-fontify-rule
1044 '([nil 3 nxml-cdata-section-delimiter] ; <![
1045 [3 8 nxml-cdata-section-CDATA] ; CDATA
1046 [8 9 nxml-cdata-section-delimiter] ; [
1047 [9 -3 nxml-cdata-section-content] ; ]]>
1048 [-3 nil nxml-cdata-section-delimiter]))
1049
1050 (put 'data
1051 'nxml-fontify-rule
1052 '([nil nil nxml-text]))
1053
1054 ;; Prolog region types in list returned by xmltok-forward-prolog.
1055
1056 (put 'xml-declaration
1057 'nxml-fontify-rule
1058 '([nil 2 nxml-processing-instruction-delimiter]
1059 [2 5 nxml-processing-instruction-target]
1060 [-2 nil nxml-processing-instruction-delimiter]))
1061
1062 (put 'xml-declaration-attribute-name
1063 'nxml-fontify-rule
1064 '([nil nil nxml-attribute-local-name]))
1065
1066 (put 'xml-declaration-attribute-value
1067 'nxml-fontify-rule
1068 '([nil 1 nxml-attribute-value-delimiter]
1069 [1 -1 nxml-attribute-value]
1070 [-1 nil nxml-attribute-value-delimiter]))
1071
1072 (put 'processing-instruction-left
1073 'nxml-fontify-rule
1074 '([nil 2 nxml-processing-instruction-delimiter]
1075 [2 nil nxml-processing-instruction-target]))
1076
1077 (put 'processing-instruction-right
1078 'nxml-fontify-rule
1079 '([nil -2 nxml-processing-instruction-content]
1080 [-2 nil nxml-processing-instruction-delimiter]))
1081
1082 (put 'literal
1083 'nxml-fontify-rule
1084 '([nil 1 nxml-prolog-literal-delimiter]
1085 [1 -1 nxml-prolog-literal-content]
1086 [-1 nil nxml-prolog-literal-delimiter]))
1087
1088 (put 'keyword
1089 'nxml-fontify-rule
1090 '([nil nil nxml-prolog-keyword]))
1091
1092 (put 'markup-declaration-open
1093 'nxml-fontify-rule
1094 '([0 2 nxml-markup-declaration-delimiter]
1095 [2 nil nxml-prolog-keyword]))
1096
1097 (put 'markup-declaration-close
1098 'nxml-fontify-rule
1099 '([nil nil nxml-markup-declaration-delimiter]))
1100
1101 (put 'internal-subset-open
1102 'nxml-fontify-rule
1103 '([nil nil nxml-markup-declaration-delimiter]))
1104
1105 (put 'internal-subset-close
1106 'nxml-fontify-rule
1107 '([nil 1 nxml-markup-declaration-delimiter]
1108 [-1 nil nxml-markup-declaration-delimiter]))
1109
1110 (put 'hash-name
1111 'nxml-fontify-rule
1112 '([nil 1 nxml-hash]
1113 [1 nil nxml-prolog-keyword]))
1114
1115 (defun nxml-apply-fontify-rule (&optional type start end)
1116 (let ((rule (get (or type xmltok-type) 'nxml-fontify-rule)))
1117 (unless start (setq start xmltok-start))
1118 (unless end (setq end (point)))
1119 (while rule
1120 (let* ((action (car rule)))
1121 (setq rule (cdr rule))
1122 (cond ((vectorp action)
1123 (nxml-set-face (let ((offset (aref action 0)))
1124 (cond ((not offset) start)
1125 ((< offset 0) (+ end offset))
1126 (t (+ start offset))))
1127 (let ((offset (aref action 1)))
1128 (cond ((not offset) end)
1129 ((< offset 0) (+ end offset))
1130 (t (+ start offset))))
1131 (aref action 2)))
1132 ((and (consp action)
1133 (eq (car action) 'element-qname))
1134 (when xmltok-name-end ; maybe nil in partial-end-tag case
1135 (nxml-fontify-qname (+ start (cdr action))
1136 xmltok-name-colon
1137 xmltok-name-end
1138 'nxml-element-prefix
1139 'nxml-element-colon
1140 'nxml-element-local-name)))
1141 ((eq action 'attributes)
1142 (nxml-fontify-attributes))
1143 ((eq action 'processing-instruction-content)
1144 (nxml-set-face (+ start 2)
1145 xmltok-name-end
1146 'nxml-processing-instruction-target)
1147 (nxml-set-face (save-excursion
1148 (goto-char xmltok-name-end)
1149 (skip-chars-forward " \t\r\n")
1150 (point))
1151 (- end 2)
1152 'nxml-processing-instruction-content))
1153 ((eq action 'char-ref)
1154 (nxml-char-ref-display-extra start
1155 end
1156 (xmltok-char-number start end)))
1157 (t (error "Invalid nxml-fontify-rule action %s" action)))))))
1158
1159 (defun nxml-fontify-attributes ()
1160 (while xmltok-namespace-attributes
1161 (nxml-fontify-attribute (car xmltok-namespace-attributes)
1162 'namespace)
1163 (setq xmltok-namespace-attributes
1164 (cdr xmltok-namespace-attributes)))
1165 (while xmltok-attributes
1166 (nxml-fontify-attribute (car xmltok-attributes))
1167 (setq xmltok-attributes
1168 (cdr xmltok-attributes))))
1169
1170 (defun nxml-fontify-attribute (att &optional namespace-declaration)
1171 (if namespace-declaration
1172 (nxml-fontify-qname (xmltok-attribute-name-start att)
1173 (xmltok-attribute-name-colon att)
1174 (xmltok-attribute-name-end att)
1175 'nxml-namespace-attribute-xmlns
1176 'nxml-namespace-attribute-colon
1177 'nxml-namespace-attribute-prefix
1178 'nxml-namespace-attribute-xmlns)
1179 (nxml-fontify-qname (xmltok-attribute-name-start att)
1180 (xmltok-attribute-name-colon att)
1181 (xmltok-attribute-name-end att)
1182 'nxml-attribute-prefix
1183 'nxml-attribute-colon
1184 'nxml-attribute-local-name))
1185 (let ((start (xmltok-attribute-value-start att))
1186 (end (xmltok-attribute-value-end att))
1187 (refs (xmltok-attribute-refs att))
1188 (delimiter-face (if namespace-declaration
1189 'nxml-namespace-attribute-value-delimiter
1190 'nxml-attribute-value-delimiter))
1191 (value-face (if namespace-declaration
1192 'nxml-namespace-attribute-value
1193 'nxml-attribute-value)))
1194 (when start
1195 (nxml-set-face (1- start) start delimiter-face)
1196 (nxml-set-face end (1+ end) delimiter-face)
1197 (while refs
1198 (let* ((ref (car refs))
1199 (ref-type (aref ref 0))
1200 (ref-start (aref ref 1))
1201 (ref-end (aref ref 2)))
1202 (nxml-set-face start ref-start value-face)
1203 (nxml-apply-fontify-rule ref-type ref-start ref-end)
1204 (setq start ref-end))
1205 (setq refs (cdr refs)))
1206 (nxml-set-face start end value-face))))
1207
1208 (defun nxml-fontify-qname (start
1209 colon
1210 end
1211 prefix-face
1212 colon-face
1213 local-name-face
1214 &optional
1215 unprefixed-face)
1216 (cond (colon (nxml-set-face start colon prefix-face)
1217 (nxml-set-face colon (1+ colon) colon-face)
1218 (nxml-set-face (1+ colon) end local-name-face))
1219 (t (nxml-set-face start end (or unprefixed-face
1220 local-name-face)))))
1221
1222 ;;; Editing
1223
1224 (defun nxml-electric-slash (arg)
1225 "Insert a slash.
1226
1227 With a prefix ARG, do nothing other than insert the slash.
1228
1229 Otherwise, if `nxml-slash-auto-complete-flag' is non-nil, insert the
1230 rest of the end-tag or empty-element if the slash is potentially part
1231 of an end-tag or the close of an empty-element.
1232
1233 If the slash is part of an end-tag that is the first non-whitespace
1234 on the line, reindent the line."
1235 (interactive "*P")
1236 (nxml-ensure-scan-up-to-date)
1237 (let* ((slash-pos (point))
1238 (end-tag-p (and (eq (char-before slash-pos) ?<)
1239 (not (nxml-get-inside slash-pos))))
1240 (at-indentation (save-excursion
1241 (back-to-indentation)
1242 (eq (point) (1- slash-pos)))))
1243 (self-insert-command (prefix-numeric-value arg))
1244 (unless arg
1245 (if nxml-slash-auto-complete-flag
1246 (if end-tag-p
1247 (condition-case err
1248 (let ((start-tag-end
1249 (nxml-scan-element-backward (1- slash-pos) t)))
1250 (when start-tag-end
1251 (insert (xmltok-start-tag-qname) ">")
1252 ;; copy the indentation of the start-tag
1253 (when (and at-indentation
1254 (save-excursion
1255 (goto-char xmltok-start)
1256 (back-to-indentation)
1257 (eq (point) xmltok-start)))
1258 (save-excursion
1259 (indent-line-to (save-excursion
1260 (goto-char xmltok-start)
1261 (current-column)))))))
1262 (nxml-scan-error nil))
1263 (when (and (eq (nxml-token-before) (point))
1264 (eq xmltok-type 'partial-empty-element))
1265 (insert ">")))
1266 (when (and end-tag-p at-indentation)
1267 (nxml-indent-line))))))
1268
1269 (defun nxml-balanced-close-start-tag-block ()
1270 "Close the start-tag before point with `>' and insert a balancing end-tag.
1271 Point is left between the start-tag and the end-tag.
1272 If there is nothing but whitespace before the `<' that opens the
1273 start-tag, then put point on a blank line, and put the end-tag on
1274 another line aligned with the start-tag."
1275 (interactive "*")
1276 (nxml-balanced-close-start-tag 'block))
1277
1278 (defun nxml-balanced-close-start-tag-inline ()
1279 "Close the start-tag before point with `>' and insert a balancing end-tag.
1280 Point is left between the start-tag and the end-tag.
1281 No extra whitespace is inserted."
1282 (interactive "*")
1283 (nxml-balanced-close-start-tag 'inline))
1284
1285 (defun nxml-balanced-close-start-tag (block-or-inline)
1286 (let ((token-end (nxml-token-before))
1287 (pos (1+ (point)))
1288 (token-start xmltok-start))
1289 (unless (or (eq xmltok-type 'partial-start-tag)
1290 (and (memq xmltok-type '(start-tag
1291 empty-element
1292 partial-empty-element))
1293 (>= token-end pos)))
1294 (error "Not in a start-tag"))
1295 ;; Note that this insertion changes xmltok-start.
1296 (insert "></"
1297 (buffer-substring-no-properties (+ xmltok-start 1)
1298 (min xmltok-name-end (point)))
1299 ">")
1300 (if (eq block-or-inline 'inline)
1301 (goto-char pos)
1302 (goto-char token-start)
1303 (back-to-indentation)
1304 (if (= (point) token-start)
1305 (let ((indent (current-column)))
1306 (goto-char pos)
1307 (insert "\n")
1308 (indent-line-to indent)
1309 (goto-char pos)
1310 (insert "\n")
1311 (indent-line-to (+ nxml-child-indent indent)))
1312 (goto-char pos)))))
1313
1314 (defun nxml-finish-element ()
1315 "Finish the current element by inserting an end-tag."
1316 (interactive "*")
1317 (nxml-finish-element-1 nil))
1318
1319 (defvar nxml-last-split-position nil
1320 "Position where `nxml-split-element' split the current element.")
1321
1322 (defun nxml-split-element ()
1323 "Split the current element by inserting an end-tag and a start-tag.
1324 Point is left after the newly inserted start-tag. When repeated,
1325 split immediately before the previously inserted start-tag and leave
1326 point unchanged."
1327 (interactive "*")
1328 (setq nxml-last-split-position
1329 (if (and (eq last-command this-command)
1330 nxml-last-split-position)
1331 (save-excursion
1332 (goto-char nxml-last-split-position)
1333 (nxml-finish-element-1 t))
1334 (nxml-finish-element-1 t))))
1335
1336 (defun nxml-finish-element-1 (startp)
1337 "Insert an end-tag for the current element and optionally a start-tag.
1338 The start-tag is inserted if STARTP is non-nil. Return the position
1339 of the inserted start-tag or nil if none was inserted."
1340 (interactive "*")
1341 (let* ((token-end (nxml-token-before))
1342 (start-tag-end
1343 (save-excursion
1344 (when (and (< (point) token-end)
1345 (memq xmltok-type
1346 '(cdata-section
1347 processing-instruction
1348 comment
1349 start-tag
1350 end-tag
1351 empty-element)))
1352 (error "Point is inside a %s"
1353 (nxml-token-type-friendly-name xmltok-type)))
1354 (nxml-scan-element-backward token-end t)))
1355 (starts-line
1356 (save-excursion
1357 (unless (eq xmltok-type 'start-tag)
1358 (error "No matching start-tag"))
1359 (goto-char xmltok-start)
1360 (back-to-indentation)
1361 (eq (point) xmltok-start)))
1362 (ends-line
1363 (save-excursion
1364 (goto-char start-tag-end)
1365 (looking-at "[ \t\r\n]*$")))
1366 (start-tag-indent (save-excursion
1367 (goto-char xmltok-start)
1368 (current-column)))
1369 (qname (xmltok-start-tag-qname))
1370 inserted-start-tag-pos)
1371 (when (and starts-line ends-line)
1372 ;; start-tag is on a line by itself
1373 ;; => put the end-tag on a line by itself
1374 (unless (<= (point)
1375 (save-excursion
1376 (back-to-indentation)
1377 (point)))
1378 (insert "\n"))
1379 (indent-line-to start-tag-indent))
1380 (insert "</" qname ">")
1381 (when startp
1382 (when starts-line
1383 (insert "\n")
1384 (indent-line-to start-tag-indent))
1385 (setq inserted-start-tag-pos (point))
1386 (insert "<" qname ">")
1387 (when (and starts-line ends-line)
1388 (insert "\n")
1389 (indent-line-to (save-excursion
1390 (goto-char xmltok-start)
1391 (forward-line 1)
1392 (back-to-indentation)
1393 (if (= (current-column)
1394 (+ start-tag-indent nxml-child-indent))
1395 (+ start-tag-indent nxml-child-indent)
1396 start-tag-indent)))))
1397 inserted-start-tag-pos))
1398
1399 ;;; Indentation
1400
1401 (defun nxml-indent-line ()
1402 "Indent current line as XML."
1403 (let* ((savep (point))
1404 (indent (condition-case nil
1405 (save-excursion
1406 (forward-line 0)
1407 (skip-chars-forward " \t")
1408 (if (>= (point) savep) (setq savep nil))
1409 (or (nxml-compute-indent) 0))
1410 (error 0))))
1411 (if (not (numberp indent))
1412 ;; If something funny is used (e.g. `noindent'), return it.
1413 indent
1414 (if (< indent 0) (setq indent 0)) ;Just in case.
1415 (if savep
1416 (save-excursion (indent-line-to indent))
1417 (indent-line-to indent)))))
1418
1419 (defun nxml-compute-indent ()
1420 "Return the indent for the line containing point."
1421 (or (nxml-compute-indent-from-matching-start-tag)
1422 (nxml-compute-indent-from-previous-line)))
1423
1424 (defun nxml-compute-indent-from-matching-start-tag ()
1425 "Compute the indent for a line with an end-tag using the matching start-tag.
1426 When the line containing point ends with an end-tag and does not start
1427 in the middle of a token, return the indent of the line containing the
1428 matching start-tag, if there is one and it occurs at the beginning of
1429 its line. Otherwise return nil."
1430 (save-excursion
1431 (back-to-indentation)
1432 (let ((bol (point)))
1433 (let ((inhibit-field-text-motion t))
1434 (end-of-line))
1435 (skip-chars-backward " \t")
1436 (and (= (nxml-token-before) (point))
1437 (memq xmltok-type '(end-tag partial-end-tag))
1438 ;; start of line must not be inside a token
1439 (or (= xmltok-start bol)
1440 (save-excursion
1441 (goto-char bol)
1442 (nxml-token-after)
1443 (= xmltok-start bol))
1444 (eq xmltok-type 'data))
1445 (condition-case err
1446 (nxml-scan-element-backward
1447 (point)
1448 nil
1449 (- (point)
1450 nxml-end-tag-indent-scan-distance))
1451 (nxml-scan-error nil))
1452 (< xmltok-start bol)
1453 (progn
1454 (goto-char xmltok-start)
1455 (skip-chars-backward " \t")
1456 (bolp))
1457 (current-indentation)))))
1458
1459 (defun nxml-compute-indent-from-previous-line ()
1460 "Compute the indent for a line using the indentation of a previous line."
1461 (save-excursion
1462 (end-of-line)
1463 (let ((eol (point))
1464 bol prev-bol ref
1465 before-context after-context)
1466 (back-to-indentation)
1467 (setq bol (point))
1468 (catch 'indent
1469 ;; Move backwards until the start of a non-blank line that is
1470 ;; not inside a token.
1471 (while (progn
1472 (when (= (forward-line -1) -1)
1473 (throw 'indent 0))
1474 (back-to-indentation)
1475 (if (looking-at "[ \t]*$")
1476 t
1477 (or prev-bol
1478 (setq prev-bol (point)))
1479 (nxml-token-after)
1480 (not (or (= xmltok-start (point))
1481 (eq xmltok-type 'data))))))
1482 (setq ref (point))
1483 ;; Now scan over tokens until the end of the line to be indented.
1484 ;; Determine the context before and after the beginning of the
1485 ;; line.
1486 (while (< (point) eol)
1487 (nxml-tokenize-forward)
1488 (cond ((<= bol xmltok-start)
1489 (setq after-context
1490 (nxml-merge-indent-context-type after-context)))
1491 ((and (<= (point) bol)
1492 (not (and (eq xmltok-type 'partial-start-tag)
1493 (= (point) bol))))
1494 (setq before-context
1495 (nxml-merge-indent-context-type before-context)))
1496 ((eq xmltok-type 'data)
1497 (setq before-context
1498 (nxml-merge-indent-context-type before-context))
1499 (setq after-context
1500 (nxml-merge-indent-context-type after-context)))
1501 ;; If in the middle of a token that looks inline,
1502 ;; then indent relative to the previous non-blank line
1503 ((eq (nxml-merge-indent-context-type before-context)
1504 'mixed)
1505 (goto-char prev-bol)
1506 (throw 'indent (current-column)))
1507 (t
1508 (throw 'indent
1509 (nxml-compute-indent-in-token bol))))
1510 (skip-chars-forward " \t\r\n"))
1511 (goto-char ref)
1512 (+ (current-column)
1513 (* nxml-child-indent
1514 (+ (if (eq before-context 'start-tag) 1 0)
1515 (if (eq after-context 'end-tag) -1 0))))))))
1516
1517 (defun nxml-merge-indent-context-type (context)
1518 "Merge the indent context type CONTEXT with the token in `xmltok-type'.
1519 Return the merged indent context type. An indent context type is
1520 either nil or one of the symbols `start-tag', `end-tag', `markup',
1521 `comment', `mixed'."
1522 (cond ((memq xmltok-type '(start-tag partial-start-tag))
1523 (if (memq context '(nil start-tag comment))
1524 'start-tag
1525 'mixed))
1526 ((memq xmltok-type '(end-tag partial-end-tag))
1527 (if (memq context '(nil end-tag comment))
1528 'end-tag
1529 'mixed))
1530 ((eq xmltok-type 'comment)
1531 (cond ((memq context '(start-tag end-tag comment))
1532 context)
1533 (context 'mixed)
1534 (t 'comment)))
1535 (context 'mixed)
1536 (t 'markup)))
1537
1538 (defun nxml-compute-indent-in-token (pos)
1539 "Return the indent for a line that starts inside a token.
1540 POS is the position of the first non-whitespace character of the line.
1541 This expects the xmltok-* variables to be set up as by `xmltok-forward'."
1542 (cond ((memq xmltok-type '(start-tag
1543 partial-start-tag
1544 empty-element
1545 partial-empty-element))
1546 (nxml-compute-indent-in-start-tag pos))
1547 ((eq xmltok-type 'comment)
1548 (nxml-compute-indent-in-delimited-token pos "<!--" "-->"))
1549 ((eq xmltok-type 'cdata-section)
1550 (nxml-compute-indent-in-delimited-token pos "<![CDATA[" "]]>"))
1551 ((eq xmltok-type 'processing-instruction)
1552 (nxml-compute-indent-in-delimited-token pos "<?" "?>"))
1553 (t
1554 (goto-char pos)
1555 (if (and (= (forward-line -1) 0)
1556 (< xmltok-start (point)))
1557 (back-to-indentation)
1558 (goto-char xmltok-start))
1559 (current-column))))
1560
1561 (defun nxml-compute-indent-in-start-tag (pos)
1562 "Return the indent for a line that starts inside a start-tag.
1563 Also for a line that starts inside an empty element.
1564 POS is the position of the first non-whitespace character of the line.
1565 This expects the xmltok-* variables to be set up as by `xmltok-forward'."
1566 (let ((value-boundary (nxml-attribute-value-boundary pos))
1567 (off 0))
1568 (if value-boundary
1569 ;; inside an attribute value
1570 (let ((value-start (car value-boundary))
1571 (value-end (cdr value-boundary)))
1572 (goto-char pos)
1573 (forward-line -1)
1574 (if (< (point) value-start)
1575 (goto-char value-start)
1576 (back-to-indentation)))
1577 ;; outside an attribute value
1578 (goto-char pos)
1579 (while (and (= (forward-line -1) 0)
1580 (nxml-attribute-value-boundary (point))))
1581 (cond ((<= (point) xmltok-start)
1582 (goto-char xmltok-start)
1583 (setq off nxml-attribute-indent)
1584 (let ((atts (xmltok-merge-attributes)))
1585 (when atts
1586 (let* ((att (car atts))
1587 (start (xmltok-attribute-name-start att)))
1588 (when (< start pos)
1589 (goto-char start)
1590 (setq off 0))))))
1591 (t
1592 (back-to-indentation))))
1593 (+ (current-column) off)))
1594
1595 (defun nxml-attribute-value-boundary (pos)
1596 "Return a pair (START . END) if POS is inside an attribute value.
1597 Otherwise return nil. START and END are the positions of the start
1598 and end of the attribute value containing POS. This expects the
1599 xmltok-* variables to be set up as by `xmltok-forward'."
1600 (let ((atts (xmltok-merge-attributes))
1601 att value-start value-end value-boundary)
1602 (while atts
1603 (setq att (car atts))
1604 (setq value-start (xmltok-attribute-value-start att))
1605 (setq value-end (xmltok-attribute-value-end att))
1606 (cond ((and value-start (< pos value-start))
1607 (setq atts nil))
1608 ((and value-start value-end (<= pos value-end))
1609 (setq value-boundary (cons value-start value-end))
1610 (setq atts nil))
1611 (t (setq atts (cdr atts)))))
1612 value-boundary))
1613
1614 (defun nxml-compute-indent-in-delimited-token (pos open-delim close-delim)
1615 "Return the indent for a line that starts inside a token with delimiters.
1616 OPEN-DELIM and CLOSE-DELIM are strings giving the opening and closing
1617 delimiters. POS is the position of the first non-whitespace character
1618 of the line. This expects the xmltok-* variables to be set up as by
1619 `xmltok-forward'."
1620 (cond ((let ((end (+ pos (length close-delim))))
1621 (and (<= end (point-max))
1622 (string= (buffer-substring-no-properties pos end)
1623 close-delim)))
1624 (goto-char xmltok-start))
1625 ((progn
1626 (goto-char pos)
1627 (forward-line -1)
1628 (<= (point) xmltok-start))
1629 (goto-char (+ xmltok-start (length open-delim)))
1630 (when (and (string= open-delim "<!--")
1631 (looking-at " "))
1632 (goto-char (1+ (point)))))
1633 (t (back-to-indentation)))
1634 (current-column))
1635
1636 ;;; Completion
1637
1638 (defun nxml-complete ()
1639 "Perform completion on the symbol preceding point.
1640
1641 Inserts as many characters as can be completed. However, if not even
1642 one character can be completed, then a buffer with the possibilities
1643 is popped up and the symbol is read from the minibuffer with
1644 completion. If the symbol is complete, then any characters that must
1645 follow the symbol are also inserted.
1646
1647 The name space used for completion and what is treated as a symbol
1648 depends on the context. The contexts in which completion is performed
1649 depend on `nxml-completion-hook'."
1650 (interactive)
1651 (unless (run-hook-with-args-until-success 'nxml-completion-hook)
1652 ;; Eventually we will complete on entity names here.
1653 (ding)
1654 (message "Cannot complete in this context")))
1655
1656 ;;; Movement
1657
1658 (defun nxml-forward-balanced-item (&optional arg)
1659 "Move forward across one balanced item.
1660 With ARG, do it that many times. Negative arg -N means
1661 move backward across N balanced expressions.
1662 This is the equivalent of `forward-sexp' for XML.
1663
1664 An element contains as items strings with no markup, tags, processing
1665 instructions, comments, CDATA sections, entity references and
1666 characters references. However, if the variable
1667 `nxml-sexp-element-flag' is non-nil, then an element is treated as a
1668 single markup item. A start-tag contains an element name followed by
1669 one or more attributes. An end-tag contains just an element name.
1670 An attribute value literals contains strings with no markup, entity
1671 references and character references. A processing instruction
1672 consists of a target and a content string. A comment or a CDATA
1673 section contains a single string. An entity reference contains a
1674 single name. A character reference contains a character number."
1675 (interactive "p")
1676 (or arg (setq arg 1))
1677 (cond ((> arg 0)
1678 (while (progn
1679 (nxml-forward-single-balanced-item)
1680 (> (setq arg (1- arg)) 0))))
1681 ((< arg 0)
1682 (while (progn
1683 (nxml-backward-single-balanced-item)
1684 (< (setq arg (1+ arg)) 0))))))
1685
1686 (defun nxml-forward-single-balanced-item ()
1687 (condition-case err
1688 (goto-char (let ((end (nxml-token-after)))
1689 (save-excursion
1690 (while (eq xmltok-type 'space)
1691 (goto-char end)
1692 (setq end (nxml-token-after)))
1693 (cond ((/= (point) xmltok-start)
1694 (nxml-scan-forward-within end))
1695 ((and nxml-sexp-element-flag
1696 (eq xmltok-type 'start-tag))
1697 ;; can't ever return nil here
1698 (nxml-scan-element-forward xmltok-start))
1699 ((and nxml-sexp-element-flag
1700 (memq xmltok-type
1701 '(end-tag partial-end-tag)))
1702 (error "Already at end of element"))
1703 (t end)))))
1704 (nxml-scan-error
1705 (goto-char (cadr err))
1706 (apply 'error (cddr err)))))
1707
1708 (defun nxml-backward-single-balanced-item ()
1709 (condition-case err
1710 (goto-char (let ((end (nxml-token-before)))
1711 (save-excursion
1712 (while (eq xmltok-type 'space)
1713 (goto-char xmltok-start)
1714 (setq end (nxml-token-before)))
1715 (cond ((/= (point) end)
1716 (nxml-scan-backward-within end))
1717 ((and nxml-sexp-element-flag
1718 (eq xmltok-type 'end-tag))
1719 ;; can't ever return nil here
1720 (nxml-scan-element-backward end)
1721 xmltok-start)
1722 ((and nxml-sexp-element-flag
1723 (eq xmltok-type 'start-tag))
1724 (error "Already at start of element"))
1725 (t xmltok-start)))))
1726 (nxml-scan-error
1727 (goto-char (cadr err))
1728 (apply 'error (cddr err)))))
1729
1730 (defun nxml-scan-forward-within (end)
1731 (setq end (- end (nxml-end-delimiter-length xmltok-type)))
1732 (when (<= end (point))
1733 (error "Already at end of %s"
1734 (nxml-token-type-friendly-name xmltok-type)))
1735 (cond ((memq xmltok-type '(start-tag
1736 empty-element
1737 partial-start-tag
1738 partial-empty-element))
1739 (if (< (point) xmltok-name-end)
1740 xmltok-name-end
1741 (let ((att (nxml-find-following-attribute)))
1742 (cond ((not att) end)
1743 ((and (xmltok-attribute-value-start att)
1744 (<= (xmltok-attribute-value-start att)
1745 (point)))
1746 (nxml-scan-forward-in-attribute-value att))
1747 ((xmltok-attribute-value-end att)
1748 (1+ (xmltok-attribute-value-end att)))
1749 ((save-excursion
1750 (goto-char (xmltok-attribute-name-end att))
1751 (looking-at "[ \t\r\n]*="))
1752 (match-end 0))
1753 (t (xmltok-attribute-name-end att))))))
1754 ((and (eq xmltok-type 'processing-instruction)
1755 (< (point) xmltok-name-end))
1756 xmltok-name-end)
1757 (t end)))
1758
1759 (defun nxml-scan-backward-within (end)
1760 (setq xmltok-start
1761 (+ xmltok-start
1762 (nxml-start-delimiter-length xmltok-type)))
1763 (when (<= (point) xmltok-start)
1764 (error "Already at start of %s"
1765 (nxml-token-type-friendly-name xmltok-type)))
1766 (cond ((memq xmltok-type '(start-tag
1767 empty-element
1768 partial-start-tag
1769 partial-empty-element))
1770 (let ((att (nxml-find-preceding-attribute)))
1771 (cond ((not att) xmltok-start)
1772 ((and (xmltok-attribute-value-start att)
1773 (<= (xmltok-attribute-value-start att)
1774 (point))
1775 (<= (point)
1776 (xmltok-attribute-value-end att)))
1777 (nxml-scan-backward-in-attribute-value att))
1778 (t (xmltok-attribute-name-start att)))))
1779 ((and (eq xmltok-type 'processing-instruction)
1780 (let ((content-start (save-excursion
1781 (goto-char xmltok-name-end)
1782 (skip-chars-forward " \r\t\n")
1783 (point))))
1784 (and (< content-start (point))
1785 content-start))))
1786 (t xmltok-start)))
1787
1788 (defun nxml-scan-forward-in-attribute-value (att)
1789 (when (= (point) (xmltok-attribute-value-end att))
1790 (error "Already at end of attribute value"))
1791 (let ((refs (xmltok-attribute-refs att))
1792 ref)
1793 (while refs
1794 (setq ref (car refs))
1795 (if (< (point) (aref ref 2))
1796 (setq refs nil)
1797 (setq ref nil)
1798 (setq refs (cdr refs))))
1799 (cond ((not ref)
1800 (xmltok-attribute-value-end att))
1801 ((< (point) (aref ref 1))
1802 (aref ref 1))
1803 ((= (point) (aref ref 1))
1804 (aref ref 2))
1805 (t
1806 (let ((end (- (aref ref 2)
1807 (nxml-end-delimiter-length (aref ref 0)))))
1808 (if (< (point) end)
1809 end
1810 (error "Already at end of %s"
1811 (nxml-token-type-friendly-name (aref ref 0)))))))))
1812
1813 (defun nxml-scan-backward-in-attribute-value (att)
1814 (when (= (point) (xmltok-attribute-value-start att))
1815 (error "Already at start of attribute value"))
1816 (let ((refs (reverse (xmltok-attribute-refs att)))
1817 ref)
1818 (while refs
1819 (setq ref (car refs))
1820 (if (< (aref ref 1) (point))
1821 (setq refs nil)
1822 (setq ref nil)
1823 (setq refs (cdr refs))))
1824 (cond ((not ref)
1825 (xmltok-attribute-value-start att))
1826 ((< (aref ref 2) (point))
1827 (aref ref 2))
1828 ((= (point) (aref ref 2))
1829 (aref ref 1))
1830 (t
1831 (let ((start (+ (aref ref 1)
1832 (nxml-start-delimiter-length (aref ref 0)))))
1833 (if (< start (point))
1834 start
1835 (error "Already at start of %s"
1836 (nxml-token-type-friendly-name (aref ref 0)))))))))
1837
1838 (defun nxml-find-following-attribute ()
1839 (let ((ret nil)
1840 (atts (or xmltok-attributes xmltok-namespace-attributes))
1841 (more-atts (and xmltok-attributes xmltok-namespace-attributes)))
1842 (while atts
1843 (let* ((att (car atts))
1844 (name-start (xmltok-attribute-name-start att)))
1845 (cond ((and (<= name-start (point))
1846 (xmltok-attribute-value-end att)
1847 ;; <= because end is before quote
1848 (<= (point) (xmltok-attribute-value-end att)))
1849 (setq atts nil)
1850 (setq ret att))
1851 ((and (< (point) name-start)
1852 (or (not ret)
1853 (< name-start
1854 (xmltok-attribute-name-start ret))))
1855 (setq ret att))))
1856 (setq atts (cdr atts))
1857 (unless atts
1858 (setq atts more-atts)
1859 (setq more-atts nil)))
1860 ret))
1861
1862 (defun nxml-find-preceding-attribute ()
1863 (let ((ret nil)
1864 (atts (or xmltok-attributes xmltok-namespace-attributes))
1865 (more-atts (and xmltok-attributes xmltok-namespace-attributes)))
1866 (while atts
1867 (let* ((att (car atts))
1868 (name-start (xmltok-attribute-name-start att)))
1869 (cond ((and (< name-start (point))
1870 (xmltok-attribute-value-end att)
1871 ;; <= because end is before quote
1872 (<= (point) (xmltok-attribute-value-end att)))
1873 (setq atts nil)
1874 (setq ret att))
1875 ((and (< name-start (point))
1876 (or (not ret)
1877 (< (xmltok-attribute-name-start ret)
1878 name-start)))
1879 (setq ret att))))
1880 (setq atts (cdr atts))
1881 (unless atts
1882 (setq atts more-atts)
1883 (setq more-atts nil)))
1884 ret))
1885
1886 (defun nxml-up-element (&optional arg)
1887 (interactive "p")
1888 (or arg (setq arg 1))
1889 (if (< arg 0)
1890 (nxml-backward-up-element (- arg))
1891 (condition-case err
1892 (while (and (> arg 0)
1893 (< (point) (point-max)))
1894 (let ((token-end (nxml-token-after)))
1895 (goto-char (cond ((or (memq xmltok-type '(end-tag
1896 partial-end-tag))
1897 (and (memq xmltok-type
1898 '(empty-element
1899 partial-empty-element))
1900 (< xmltok-start (point))))
1901 token-end)
1902 ((nxml-scan-element-forward
1903 (if (and (eq xmltok-type 'start-tag)
1904 (= (point) xmltok-start))
1905 xmltok-start
1906 token-end)
1907 t))
1908 (t (error "No parent element")))))
1909 (setq arg (1- arg)))
1910 (nxml-scan-error
1911 (goto-char (cadr err))
1912 (apply 'error (cddr err))))))
1913
1914 (defun nxml-backward-up-element (&optional arg)
1915 (interactive "p")
1916 (or arg (setq arg 1))
1917 (if (< arg 0)
1918 (nxml-up-element (- arg))
1919 (condition-case err
1920 (while (and (> arg 0)
1921 (< (point-min) (point)))
1922 (let ((token-end (nxml-token-before)))
1923 (goto-char (cond ((or (memq xmltok-type '(start-tag
1924 partial-start-tag))
1925 (and (memq xmltok-type
1926 '(empty-element
1927 partial-empty-element))
1928 (< (point) token-end)))
1929 xmltok-start)
1930 ((nxml-scan-element-backward
1931 (if (and (eq xmltok-type 'end-tag)
1932 (= (point) token-end))
1933 token-end
1934 xmltok-start)
1935 t)
1936 xmltok-start)
1937 (t (error "No parent element")))))
1938 (setq arg (1- arg)))
1939 (nxml-scan-error
1940 (goto-char (cadr err))
1941 (apply 'error (cddr err))))))
1942
1943 (defun nxml-down-element (&optional arg)
1944 "Move forward down into the content of an element.
1945 With ARG, do this that many times.
1946 Negative ARG means move backward but still down."
1947 (interactive "p")
1948 (or arg (setq arg 1))
1949 (if (< arg 0)
1950 (nxml-backward-down-element (- arg))
1951 (while (> arg 0)
1952 (goto-char
1953 (let ((token-end (nxml-token-after)))
1954 (save-excursion
1955 (goto-char token-end)
1956 (while (progn
1957 (when (memq xmltok-type '(nil end-tag partial-end-tag))
1958 (error "No following start-tags in this element"))
1959 (not (memq xmltok-type '(start-tag partial-start-tag))))
1960 (nxml-tokenize-forward))
1961 (point))))
1962 (setq arg (1- arg)))))
1963
1964 (defun nxml-backward-down-element (&optional arg)
1965 (interactive "p")
1966 (or arg (setq arg 1))
1967 (if (< arg 0)
1968 (nxml-down-element (- arg))
1969 (while (> arg 0)
1970 (goto-char
1971 (save-excursion
1972 (nxml-token-before)
1973 (goto-char xmltok-start)
1974 (while (progn
1975 (when (memq xmltok-type '(start-tag
1976 partial-start-tag
1977 prolog
1978 nil))
1979 (error "No preceding end-tags in this element"))
1980 (not (memq xmltok-type '(end-tag partial-end-tag))))
1981 (if (or (<= (point) nxml-prolog-end)
1982 (not (search-backward "<" nxml-prolog-end t)))
1983 (setq xmltok-type nil)
1984 (nxml-move-outside-backwards)
1985 (xmltok-forward)))
1986 xmltok-start))
1987 (setq arg (1- arg)))))
1988
1989 (defun nxml-forward-element (&optional arg)
1990 "Move forward over one element.
1991 With ARG, do it that many times.
1992 Negative ARG means move backward."
1993 (interactive "p")
1994 (or arg (setq arg 1))
1995 (if (< arg 0)
1996 (nxml-backward-element (- arg))
1997 (condition-case err
1998 (while (and (> arg 0)
1999 (< (point) (point-max)))
2000 (goto-char
2001 (or (nxml-scan-element-forward (nxml-token-before))
2002 (error "No more elements")))
2003 (setq arg (1- arg)))
2004 (nxml-scan-error
2005 (goto-char (cadr err))
2006 (apply 'error (cddr err))))))
2007
2008 (defun nxml-backward-element (&optional arg)
2009 "Move backward over one element.
2010 With ARG, do it that many times.
2011 Negative ARG means move forward."
2012 (interactive "p")
2013 (or arg (setq arg 1))
2014 (if (< arg 0)
2015 (nxml-forward-element (- arg))
2016 (condition-case err
2017 (while (and (> arg 0)
2018 (< (point-min) (point)))
2019 (goto-char
2020 (or (and (nxml-scan-element-backward (progn
2021 (nxml-token-after)
2022 xmltok-start))
2023 xmltok-start)
2024 (error "No preceding elements")))
2025 (setq arg (1- arg)))
2026 (nxml-scan-error
2027 (goto-char (cadr err))
2028 (apply 'error (cddr err))))))
2029
2030 (defun nxml-mark-token-after ()
2031 (interactive)
2032 (push-mark (nxml-token-after) nil t)
2033 (goto-char xmltok-start)
2034 (message "Marked %s" xmltok-type))
2035
2036 ;;; Paragraphs
2037
2038 (defun nxml-mark-paragraph ()
2039 "Put point at beginning of this paragraph, mark at end.
2040 The paragraph marked is the one that contains point or follows point."
2041 (interactive)
2042 (nxml-forward-paragraph)
2043 (push-mark nil t t)
2044 (nxml-backward-paragraph))
2045
2046 (defun nxml-forward-paragraph (&optional arg)
2047 (interactive "p")
2048 (or arg (setq arg 1))
2049 (cond ((< arg 0)
2050 (nxml-backward-paragraph (- arg)))
2051 ((> arg 0)
2052 (forward-line 0)
2053 (while (and (nxml-forward-single-paragraph)
2054 (> (setq arg (1- arg)) 0))))))
2055
2056 (defun nxml-backward-paragraph (&optional arg)
2057 (interactive "p")
2058 (or arg (setq arg 1))
2059 (cond ((< arg 0)
2060 (nxml-forward-paragraph (- arg)))
2061 ((> arg 0)
2062 (unless (bolp)
2063 (let ((inhibit-field-text-motion t))
2064 (end-of-line)))
2065 (while (and (nxml-backward-single-paragraph)
2066 (> (setq arg (1- arg)) 0))))))
2067
2068 (defun nxml-forward-single-paragraph ()
2069 "Move forward over a single paragraph.
2070 Return nil at end of buffer, t otherwise."
2071 (let* ((token-end (nxml-token-after))
2072 (offset (- (point) xmltok-start))
2073 pos had-data)
2074 (goto-char token-end)
2075 (while (and (< (point) (point-max))
2076 (not (setq pos
2077 (nxml-paragraph-end-pos had-data offset))))
2078 (when (nxml-token-contains-data-p offset)
2079 (setq had-data t))
2080 (nxml-tokenize-forward)
2081 (setq offset 0))
2082 (when pos (goto-char pos))))
2083
2084 (defun nxml-backward-single-paragraph ()
2085 "Move backward over a single paragraph.
2086 Return nil at start of buffer, t otherwise."
2087 (let* ((token-end (nxml-token-before))
2088 (offset (- token-end (point)))
2089 (last-tag-pos xmltok-start)
2090 pos had-data last-data-pos)
2091 (goto-char token-end)
2092 (unless (setq pos (nxml-paragraph-start-pos nil offset))
2093 (setq had-data (nxml-token-contains-data-p nil offset))
2094 (goto-char xmltok-start)
2095 (while (and (not pos) (< (point-min) (point)))
2096 (cond ((search-backward "<" nxml-prolog-end t)
2097 (nxml-move-outside-backwards)
2098 (save-excursion
2099 (while (< (point) last-tag-pos)
2100 (xmltok-forward)
2101 (when (and (not had-data) (nxml-token-contains-data-p))
2102 (setq pos nil)
2103 (setq last-data-pos xmltok-start))
2104 (let ((tem (nxml-paragraph-start-pos had-data 0)))
2105 (when tem (setq pos tem)))))
2106 (when (and (not had-data) last-data-pos (not pos))
2107 (setq had-data t)
2108 (save-excursion
2109 (while (< (point) last-data-pos)
2110 (xmltok-forward))
2111 (let ((tem (nxml-paragraph-start-pos had-data 0)))
2112 (when tem (setq pos tem)))))
2113 (setq last-tag-pos (point)))
2114 (t (goto-char (point-min))))))
2115 (when pos (goto-char pos))))
2116
2117 (defun nxml-token-contains-data-p (&optional start end)
2118 (setq start (+ xmltok-start (or start 0)))
2119 (setq end (- (point) (or end 0)))
2120 (when (eq xmltok-type 'cdata-section)
2121 (setq start (max start (+ xmltok-start 9)))
2122 (setq end (min end (- (point) 3))))
2123 (or (and (eq xmltok-type 'data)
2124 (eq start xmltok-start)
2125 (eq end (point)))
2126 (eq xmltok-type 'char-ref)
2127 (and (memq xmltok-type '(data cdata-section))
2128 (< start end)
2129 (save-excursion
2130 (goto-char start)
2131 (re-search-forward "[^ \t\r\n]" end t)))))
2132
2133 (defun nxml-paragraph-end-pos (had-data offset)
2134 "Return the position of the paragraph end if contained in the current token.
2135 Return nil if the current token does not contain the paragraph end.
2136 Only characters after OFFSET from the start of the token are eligible.
2137 HAD-DATA says whether there have been non-whitespace data characters yet."
2138 (cond ((not had-data)
2139 (cond ((memq xmltok-type '(data cdata-section))
2140 (save-excursion
2141 (let ((end (point)))
2142 (goto-char (+ xmltok-start
2143 (max (if (eq xmltok-type 'cdata-section)
2144 9
2145 0)
2146 offset)))
2147 (and (re-search-forward "[^ \t\r\n]" end t)
2148 (re-search-forward "^[ \t]*$" end t)
2149 (match-beginning 0)))))
2150 ((and (eq xmltok-type 'comment)
2151 (nxml-token-begins-line-p)
2152 (nxml-token-ends-line-p))
2153 (save-excursion
2154 (let ((end (point)))
2155 (goto-char (+ xmltok-start (max 4 offset)))
2156 (when (re-search-forward "[^ \t\r\n]" (- end 3) t)
2157 (if (re-search-forward "^[ \t]*$" end t)
2158 (match-beginning 0)
2159 (goto-char (- end 3))
2160 (skip-chars-backward " \t")
2161 (unless (bolp)
2162 (beginning-of-line 2))
2163 (point))))))))
2164 ((memq xmltok-type '(data space cdata-section))
2165 (save-excursion
2166 (let ((end (point)))
2167 (goto-char (+ xmltok-start offset))
2168 (and (re-search-forward "^[ \t]*$" end t)
2169 (match-beginning 0)))))
2170 ((and (memq xmltok-type '(start-tag
2171 end-tag
2172 empty-element
2173 comment
2174 processing-instruction
2175 entity-ref))
2176 (nxml-token-begins-line-p)
2177 (nxml-token-ends-line-p))
2178 (save-excursion
2179 (goto-char xmltok-start)
2180 (skip-chars-backward " \t")
2181 (point)))
2182 ((and (eq xmltok-type 'end-tag)
2183 (looking-at "[ \t]*$")
2184 (not (nxml-in-mixed-content-p t)))
2185 (save-excursion
2186 (or (search-forward "\n" nil t)
2187 (point-max))))))
2188
2189 (defun nxml-paragraph-start-pos (had-data offset)
2190 "Return the position of the paragraph start if contained in the current token.
2191 Return nil if the current token does not contain the paragraph start.
2192 Only characters before OFFSET from the end of the token are eligible.
2193 HAD-DATA says whether there have been non-whitespace data characters yet."
2194 (cond ((not had-data)
2195 (cond ((memq xmltok-type '(data cdata-section))
2196 (save-excursion
2197 (goto-char (- (point)
2198 (max (if (eq xmltok-type 'cdata-section)
2199 3
2200 0)
2201 offset)))
2202 (and (re-search-backward "[^ \t\r\n]" xmltok-start t)
2203 (re-search-backward "^[ \t]*$" xmltok-start t)
2204 (match-beginning 0))))
2205 ((and (eq xmltok-type 'comment)
2206 (nxml-token-ends-line-p)
2207 (nxml-token-begins-line-p))
2208 (save-excursion
2209 (goto-char (- (point) (max 3 offset)))
2210 (when (and (< (+ xmltok-start 4) (point))
2211 (re-search-backward "[^ \t\r\n]"
2212 (+ xmltok-start 4)
2213 t))
2214 (if (re-search-backward "^[ \t]*$" xmltok-start t)
2215 (match-beginning 0)
2216 (goto-char xmltok-start)
2217 (if (looking-at "<!--[ \t]*\n")
2218 (match-end 0)
2219 (skip-chars-backward " \t")
2220 (point))))))))
2221 ((memq xmltok-type '(data space cdata-section))
2222 (save-excursion
2223 (goto-char (- (point) offset))
2224 (and (re-search-backward "^[ \t]*$" xmltok-start t)
2225 (match-beginning 0))))
2226 ((and (memq xmltok-type '(start-tag
2227 end-tag
2228 empty-element
2229 comment
2230 processing-instruction
2231 entity-ref))
2232 (nxml-token-ends-line-p)
2233 (nxml-token-begins-line-p))
2234 (or (search-forward "\n" nil t)
2235 (point-max)))
2236 ((and (eq xmltok-type 'start-tag)
2237 (nxml-token-begins-line-p)
2238 (not (save-excursion
2239 (goto-char xmltok-start)
2240 (nxml-in-mixed-content-p nil))))
2241 (save-excursion
2242 (goto-char xmltok-start)
2243 (skip-chars-backward " \t")
2244 ;; include any blank line before
2245 (or (and (eq (char-before) ?\n)
2246 (save-excursion
2247 (goto-char (1- (point)))
2248 (skip-chars-backward " \t")
2249 (and (bolp) (point))))
2250 (point))))))
2251
2252 (defun nxml-token-ends-line-p () (looking-at "[ \t]*$"))
2253
2254 (defun nxml-token-begins-line-p ()
2255 (save-excursion
2256 (goto-char xmltok-start)
2257 (skip-chars-backward " \t")
2258 (bolp)))
2259
2260 (defun nxml-in-mixed-content-p (endp)
2261 "Return non-nil if point is in mixed content.
2262 Point must be after an end-tag or before a start-tag.
2263 ENDP is t in the former case, nil in the latter."
2264 (let (matching-tag-pos)
2265 (cond ((not (run-hook-with-args-until-failure
2266 'nxml-in-mixed-content-hook))
2267 nil)
2268 ;; See if the matching tag does not start or end a line.
2269 ((condition-case err
2270 (progn
2271 (setq matching-tag-pos
2272 (xmltok-save
2273 (if endp
2274 (and (nxml-scan-element-backward (point))
2275 xmltok-start)
2276 (nxml-scan-element-forward (point)))))
2277 (and matching-tag-pos
2278 (save-excursion
2279 (goto-char matching-tag-pos)
2280 (not (if endp
2281 (progn
2282 (skip-chars-backward " \t")
2283 (bolp))
2284 (looking-at "[ \t]*$"))))))
2285 (nxml-scan-error nil))
2286 t)
2287 ;; See if there's data at the same level.
2288 ((let (start end)
2289 (if endp
2290 (setq start matching-tag-pos
2291 end (point))
2292 (setq start (point)
2293 end matching-tag-pos))
2294 (save-excursion
2295 (or (when start
2296 (goto-char start)
2297 (nxml-preceding-sibling-data-p))
2298 (when end
2299 (goto-char end)
2300 (nxml-following-sibling-data-p)))))
2301 t)
2302 ;; Otherwise, treat as not mixed
2303 (t nil))))
2304
2305 (defun nxml-preceding-sibling-data-p ()
2306 "Return non-nil if there is a previous sibling that is data."
2307 (let ((lim (max (- (point) nxml-mixed-scan-distance)
2308 nxml-prolog-end))
2309 (level 0)
2310 found end)
2311 (xmltok-save
2312 (save-excursion
2313 (while (and (< lim (point))
2314 (>= level 0)
2315 (not found)
2316 (progn
2317 (setq end (point))
2318 (search-backward "<" lim t)))
2319 (nxml-move-outside-backwards)
2320 (save-excursion
2321 (xmltok-forward)
2322 (let ((prev-level level))
2323 (cond ((eq xmltok-type 'end-tag)
2324 (setq level (1+ level)))
2325 ((eq xmltok-type 'start-tag)
2326 (setq level (1- level))))
2327 (when (eq prev-level 0)
2328 (while (and (< (point) end) (not found))
2329 (xmltok-forward)
2330 (when (memq xmltok-type '(data cdata-section char-ref))
2331 (setq found t)))))))))
2332 found))
2333
2334 (defun nxml-following-sibling-data-p ()
2335 (let ((lim (min (+ (point) nxml-mixed-scan-distance)
2336 (point-max)))
2337 (level 0)
2338 found)
2339 (xmltok-save
2340 (save-excursion
2341 (while (and (< (point) lim)
2342 (>= level 0)
2343 (nxml-tokenize-forward)
2344 (not found))
2345 (cond ((eq xmltok-type 'start-tag)
2346 (setq level (1+ level)))
2347 ((eq xmltok-type 'end-tag)
2348 (setq level (1- level)))
2349 ((and (eq level 0)
2350 (memq xmltok-type '(data cdata-section char-ref)))
2351 (setq found t))))))
2352 found))
2353
2354 ;;; Filling
2355
2356 (defun nxml-do-fill-paragraph (arg)
2357 (let (fill-paragraph-function
2358 fill-prefix
2359 start end)
2360 (save-excursion
2361 (nxml-forward-paragraph)
2362 (setq end (point))
2363 (nxml-backward-paragraph)
2364 (skip-chars-forward " \t\r\n")
2365 (setq start (point))
2366 (beginning-of-line)
2367 (setq fill-prefix (buffer-substring-no-properties (point) start))
2368 (when (and (not (nxml-get-inside (point)))
2369 (looking-at "[ \t]*<!--"))
2370 (setq fill-prefix (concat fill-prefix " ")))
2371 (fill-region-as-paragraph start end arg))
2372 (skip-line-prefix fill-prefix)
2373 fill-prefix))
2374
2375 (defun nxml-newline-and-indent (soft)
2376 (delete-horizontal-space)
2377 (if soft (insert-and-inherit ?\n) (newline 1))
2378 (nxml-indent-line))
2379
2380
2381 ;;; Dynamic markup
2382
2383 (defvar nxml-dynamic-markup-prev-pos nil)
2384 (defvar nxml-dynamic-markup-prev-lengths nil)
2385 (defvar nxml-dynamic-markup-prev-found-marker nil)
2386 (defvar nxml-dynamic-markup-prev-start-tags (make-hash-table :test 'equal))
2387
2388 (defun nxml-dynamic-markup-word ()
2389 "Dynamically markup the word before point.
2390 This attempts to find a tag to put around the word before point based
2391 on the contents of the current buffer. The end-tag will be inserted at
2392 point. The start-tag will be inserted at or before the beginning of
2393 the word before point; the contents of the current buffer is used to
2394 decide where.
2395
2396 It works in a similar way to \\[dabbrev-expand]. It searches first
2397 backwards from point, then forwards from point for an element whose
2398 content is a string which matches the contents of the buffer before
2399 point and which includes at least the word before point. It then
2400 copies the start- and end-tags from that element and uses them to
2401 surround the matching string before point.
2402
2403 Repeating \\[nxml-dynamic-markup-word] immediately after successful
2404 \\[nxml-dynamic-markup-word] removes the previously inserted markup
2405 and attempts to find another possible way to do the markup."
2406 (interactive "*")
2407 (let (search-start-pos done)
2408 (if (and (integerp nxml-dynamic-markup-prev-pos)
2409 (= nxml-dynamic-markup-prev-pos (point))
2410 (eq last-command this-command)
2411 nxml-dynamic-markup-prev-lengths)
2412 (let* ((end-tag-open-pos
2413 (- nxml-dynamic-markup-prev-pos
2414 (nth 2 nxml-dynamic-markup-prev-lengths)))
2415 (start-tag-close-pos
2416 (- end-tag-open-pos
2417 (nth 1 nxml-dynamic-markup-prev-lengths)))
2418 (start-tag-open-pos
2419 (- start-tag-close-pos
2420 (nth 0 nxml-dynamic-markup-prev-lengths))))
2421 (delete-region end-tag-open-pos nxml-dynamic-markup-prev-pos)
2422 (delete-region start-tag-open-pos start-tag-close-pos)
2423 (setq search-start-pos
2424 (marker-position nxml-dynamic-markup-prev-found-marker)))
2425 (clrhash nxml-dynamic-markup-prev-start-tags))
2426 (setq nxml-dynamic-markup-prev-pos nil)
2427 (setq nxml-dynamic-markup-prev-lengths nil)
2428 (setq nxml-dynamic-markup-prev-found-marker nil)
2429 (goto-char
2430 (save-excursion
2431 (let* ((pos (point))
2432 (word (progn
2433 (backward-word 1)
2434 (unless (< (point) pos)
2435 (error "No word to markup"))
2436 (buffer-substring-no-properties (point) pos)))
2437 (search (concat word "</"))
2438 done)
2439 (when search-start-pos
2440 (goto-char search-start-pos))
2441 (while (and (not done)
2442 (or (and (< (point) pos)
2443 (or (search-backward search nil t)
2444 (progn (goto-char pos) nil)))
2445 (search-forward search nil t)))
2446 (goto-char (- (match-end 0) 2))
2447 (setq done (nxml-try-copy-markup pos)))
2448 (or done
2449 (error (if (zerop (hash-table-count
2450 nxml-dynamic-markup-prev-start-tags))
2451 "No possible markup found for `%s'"
2452 "No more markup possibilities found for `%s'")
2453 word)))))))
2454
2455 (defun nxml-try-copy-markup (word-end-pos)
2456 (save-excursion
2457 (let ((end-tag-pos (point)))
2458 (when (and (not (nxml-get-inside end-tag-pos))
2459 (search-backward "<" nil t)
2460 (not (nxml-get-inside (point))))
2461 (xmltok-forward)
2462 (when (and (eq xmltok-type 'start-tag)
2463 (< (point) end-tag-pos))
2464 (let* ((start-tag-close-pos (point))
2465 (start-tag
2466 (buffer-substring-no-properties xmltok-start
2467 start-tag-close-pos))
2468 (words
2469 (nreverse
2470 (split-string
2471 (buffer-substring-no-properties start-tag-close-pos
2472 end-tag-pos)
2473 "[ \t\r\n]+"))))
2474 (goto-char word-end-pos)
2475 (while (and words
2476 (re-search-backward (concat
2477 (regexp-quote (car words))
2478 "\\=")
2479 nil
2480 t))
2481 (setq words (cdr words))
2482 (skip-chars-backward " \t\r\n"))
2483 (when (and (not words)
2484 (progn
2485 (skip-chars-forward " \t\r\n")
2486 (not (gethash (cons (point) start-tag)
2487 nxml-dynamic-markup-prev-start-tags)))
2488 (or (< end-tag-pos (point))
2489 (< word-end-pos xmltok-start)))
2490 (setq nxml-dynamic-markup-prev-found-marker
2491 (copy-marker end-tag-pos t))
2492 (puthash (cons (point) start-tag)
2493 t
2494 nxml-dynamic-markup-prev-start-tags)
2495 (setq nxml-dynamic-markup-prev-lengths
2496 (list (- start-tag-close-pos xmltok-start)
2497 (- word-end-pos (point))
2498 (+ (- xmltok-name-end xmltok-start) 2)))
2499 (let ((name (xmltok-start-tag-qname)))
2500 (insert start-tag)
2501 (goto-char (+ word-end-pos
2502 (- start-tag-close-pos xmltok-start)))
2503 (insert "</" name ">")
2504 (setq nxml-dynamic-markup-prev-pos (point))))))))))
2505
2506
2507 ;;; Character names
2508
2509 (defvar nxml-char-name-ignore-case t)
2510
2511 (defvar nxml-char-name-alist nil
2512 "Alist of character names.
2513 Each member of the list has the form (NAME CODE . NAMESET),
2514 where NAME is a string naming a character, NAMESET is a symbol
2515 identifying a set of names and CODE is an integer specifying the
2516 Unicode scalar value of the named character.
2517 The NAME will only be used for completion if NAMESET has
2518 a non-nil `nxml-char-name-set-enabled' property.
2519 If NAMESET does does not have `nxml-char-name-set-defined' property,
2520 then it must have a `nxml-char-name-set-file' property and `load'
2521 will be applied to the value of this property if the nameset
2522 is enabled.")
2523
2524 (defvar nxml-char-name-table (make-hash-table :test 'eq)
2525 "Hash table for mapping char codes to names.
2526 Each key is a Unicode scalar value.
2527 Each value is a list of pairs of the form (NAMESET . NAME),
2528 where NAMESET is a symbol identifying a set of names,
2529 and NAME is a string naming a character.")
2530
2531 (defvar nxml-autoload-char-name-set-list nil
2532 "List of char namesets that can be autoloaded.")
2533
2534 (defun nxml-enable-char-name-set (nameset)
2535 (put nameset 'nxml-char-name-set-enabled t))
2536
2537 (defun nxml-disable-char-name-set (nameset)
2538 (put nameset 'nxml-char-name-set-enabled nil))
2539
2540 (defun nxml-char-name-set-enabled-p (nameset)
2541 (get nameset 'nxml-char-name-set-enabled))
2542
2543 (defun nxml-autoload-char-name-set (nameset file)
2544 (unless (memq nameset nxml-autoload-char-name-set-list)
2545 (setq nxml-autoload-char-name-set-list
2546 (cons nameset nxml-autoload-char-name-set-list)))
2547 (put nameset 'nxml-char-name-set-file file))
2548
2549 (defun nxml-define-char-name-set (nameset alist)
2550 "Define a set of character names.
2551 NAMESET is a symbol identifying the set.
2552 ALIST is a list where each member has the form (NAME CODE),
2553 where NAME is a string naming a character and code is an
2554 integer giving the Unicode scalar value of the character."
2555 (when (get nameset 'nxml-char-name-set-defined)
2556 (error "Nameset `%s' already defined" nameset))
2557 (let ((iter alist))
2558 (while iter
2559 (let* ((name-code (car iter))
2560 (name (car name-code))
2561 (code (cadr name-code)))
2562 (puthash code
2563 (cons (cons nameset name)
2564 (gethash code nxml-char-name-table))
2565 nxml-char-name-table))
2566 (setcdr (cdr (car iter)) nameset)
2567 (setq iter (cdr iter))))
2568 (setq nxml-char-name-alist
2569 (nconc alist nxml-char-name-alist))
2570 (put nameset 'nxml-char-name-set-defined t))
2571
2572 (defun nxml-get-char-name (code)
2573 (mapc 'nxml-maybe-load-char-name-set nxml-autoload-char-name-set-list)
2574 (let ((names (gethash code nxml-char-name-table))
2575 name)
2576 (while (and names (not name))
2577 (if (nxml-char-name-set-enabled-p (caar names))
2578 (setq name (cdar names))
2579 (setq names (cdr names))))
2580 name))
2581
2582 (defvar nxml-named-char-history nil)
2583
2584 (defun nxml-insert-named-char (arg)
2585 "Insert a character using its name.
2586 The name is read from the minibuffer.
2587 Normally, inserts the character as a numeric character reference.
2588 With a prefix argument, inserts the character directly."
2589 (interactive "*P")
2590 (mapc 'nxml-maybe-load-char-name-set nxml-autoload-char-name-set-list)
2591 (let ((name
2592 (let ((completion-ignore-case nxml-char-name-ignore-case))
2593 (completing-read "Character name: "
2594 nxml-char-name-alist
2595 (lambda (member)
2596 (get (cddr member) 'nxml-char-name-set-enabled))
2597 t
2598 nil
2599 'nxml-named-char-history)))
2600 (alist nxml-char-name-alist)
2601 elt code)
2602 (while (and alist (not code))
2603 (setq elt (assoc name alist))
2604 (if (get (cddr elt) 'nxml-char-name-set-enabled)
2605 (setq code (cadr elt))
2606 (setq alist (cdr (member elt alist)))))
2607 (when code
2608 (insert (if arg
2609 (or (decode-char 'ucs code)
2610 (error "Character %x is not supported by Emacs"
2611 code))
2612 (format "&#x%X;" code))))))
2613
2614 (defun nxml-maybe-load-char-name-set (sym)
2615 (when (and (get sym 'nxml-char-name-set-enabled)
2616 (not (get sym 'nxml-char-name-set-defined))
2617 (stringp (get sym 'nxml-char-name-set-file)))
2618 (load (get sym 'nxml-char-name-set-file))))
2619
2620 (defun nxml-toggle-char-ref-extra-display (arg)
2621 "Toggle the display of extra information for character references."
2622 (interactive "P")
2623 (let ((new (if (null arg)
2624 (not nxml-char-ref-extra-display)
2625 (> (prefix-numeric-value arg) 0))))
2626 (when (not (eq new nxml-char-ref-extra-display))
2627 (setq nxml-char-ref-extra-display new)
2628 (font-lock-fontify-buffer))))
2629
2630 (put 'nxml-char-ref 'evaporate t)
2631
2632 (defun nxml-char-ref-display-extra (start end n)
2633 (when nxml-char-ref-extra-display
2634 (let ((name (nxml-get-char-name n))
2635 (glyph-string (and nxml-char-ref-display-glyph-flag
2636 (nxml-glyph-display-string n 'nxml-glyph)))
2637 ov)
2638 (when (or name glyph-string)
2639 (setq ov (make-overlay start end nil t))
2640 (overlay-put ov 'category 'nxml-char-ref)
2641 (when name
2642 (overlay-put ov 'help-echo name))
2643 (when glyph-string
2644 (overlay-put ov
2645 'after-string
2646 (propertize glyph-string 'face 'nxml-glyph)))))))
2647
2648 (defun nxml-clear-char-ref-extra-display (start end)
2649 (let ((ov (overlays-in start end)))
2650 (while ov
2651 (when (eq (overlay-get (car ov) 'category) 'nxml-char-ref)
2652 (delete-overlay (car ov)))
2653 (setq ov (cdr ov)))))
2654
2655
2656 (defun nxml-start-delimiter-length (type)
2657 (or (get type 'nxml-start-delimiter-length)
2658 0))
2659
2660 (put 'cdata-section 'nxml-start-delimiter-length 9)
2661 (put 'comment 'nxml-start-delimiter-length 4)
2662 (put 'processing-instruction 'nxml-start-delimiter-length 2)
2663 (put 'start-tag 'nxml-start-delimiter-length 1)
2664 (put 'empty-element 'nxml-start-delimiter-length 1)
2665 (put 'partial-empty-element 'nxml-start-delimiter-length 1)
2666 (put 'entity-ref 'nxml-start-delimiter-length 1)
2667 (put 'char-ref 'nxml-start-delimiter-length 2)
2668
2669 (defun nxml-end-delimiter-length (type)
2670 (or (get type 'nxml-end-delimiter-length)
2671 0))
2672
2673 (put 'cdata-section 'nxml-end-delimiter-length 3)
2674 (put 'comment 'nxml-end-delimiter-length 3)
2675 (put 'processing-instruction 'nxml-end-delimiter-length 2)
2676 (put 'start-tag 'nxml-end-delimiter-length 1)
2677 (put 'empty-element 'nxml-end-delimiter-length 2)
2678 (put 'partial-empty-element 'nxml-end-delimiter-length 1)
2679 (put 'entity-ref 'nxml-end-delimiter-length 1)
2680 (put 'char-ref 'nxml-end-delimiter-length 1)
2681
2682 (defun nxml-token-type-friendly-name (type)
2683 (or (get type 'nxml-friendly-name)
2684 (symbol-name type)))
2685
2686 (put 'cdata-section 'nxml-friendly-name "CDATA section")
2687 (put 'processing-instruction 'nxml-friendly-name "processing instruction")
2688 (put 'entity-ref 'nxml-friendly-name "entity reference")
2689 (put 'char-ref 'nxml-friendly-name "character reference")
2690
2691 ;;;###autoload
2692 (defalias 'xml-mode 'nxml-mode)
2693
2694 (provide 'nxml-mode)
2695
2696 ;; arch-tag: 8603bc5f-1ef9-4021-b223-322fb2ca708e
2697 ;;; nxml-mode.el ends here