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