(rng-preferred-prefix-alist): Add some defaults.
[bpt/emacs.git] / lisp / nxml / nxml-mode.el
CommitLineData
8cd39fb3
MH
1;;; nxml-mode.el --- a new XML mode
2
dcb8ac09 3;; Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
8cd39fb3
MH
4
5;; Author: James Clark
6;; Keywords: XML
7
e290ff07 8;; This file is part of GNU Emacs.
8cd39fb3 9
e290ff07
GM
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.
8cd39fb3 14
e290ff07
GM
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.
8cd39fb3
MH
24
25;;; Commentary:
26
8cd39fb3
MH
27;; See nxml-rap.el for description of parsing strategy.
28
29;; The font locking here is independent of font-lock.el. We want to
30;; do more sophisticated handling of changes and we want to use the
31;; same xmltok rather than regexps for parsing so that we parse
32;; consistently and correctly.
33
34;;; Code:
35
36(when (featurep 'mucs)
37 (error "nxml-mode is not compatible with Mule-UCS"))
38
39(require 'xmltok)
40(require 'nxml-enc)
41(require 'nxml-glyph)
42(require 'nxml-util)
43(require 'nxml-rap)
44(require 'nxml-outln)
45
46;;; Customization
47
48(defgroup nxml nil
49 "New XML editing mode"
50 :group 'languages
51 :group 'wp)
52
92bbfd0d 53(defgroup nxml-faces nil
8cd39fb3
MH
54 "Faces for XML syntax highlighting."
55 :group 'nxml
92bbfd0d 56 :group 'font-lock-faces)
8cd39fb3
MH
57
58(defcustom nxml-syntax-highlight-flag t
59 "*Non-nil means nxml-mode should perform syntax highlighting."
60 :group 'nxml
61 :type 'boolean)
62
63(defcustom nxml-char-ref-display-glyph-flag t
64 "*Non-nil means display glyph following character reference.
92bbfd0d 65The glyph is displayed in face `nxml-glyph'. The hook
8cd39fb3
MH
66`nxml-glyph-set-hook' can be used to customize for which characters
67glyphs are displayed."
68 :group 'nxml
69 :type 'boolean)
70
71(defcustom nxml-mode-hook nil
72 "Hook run by command `nxml-mode'."
73 :group 'nxml
74 :type 'hook)
75
76(defcustom nxml-sexp-element-flag nil
77 "*Non-nil means sexp commands treat an element as a single expression."
78 :group 'nxml
79 :type 'boolean)
80
81(defcustom nxml-slash-auto-complete-flag nil
82 "*Non-nil means typing a slash automatically completes the end-tag.
83This is used by `nxml-electric-slash'."
84 :group 'nxml
85 :type 'boolean)
86
87(defcustom nxml-child-indent 2
88 "*Indentation for the children of an element relative to the start-tag.
89This only applies when the line or lines containing the start-tag contains
90nothing else other than that start-tag."
91 :group 'nxml
92 :type 'integer)
93
94(defcustom nxml-attribute-indent 4
95 "*Indentation for the attributes of an element relative to the start-tag.
96This only applies when the first attribute of a tag starts a line. In other
97cases, the first attribute on one line is indented the same as the first
98attribute on the previous line."
99 :group 'nxml
100 :type 'integer)
101
102(defvar nxml-fontify-chunk-size 500)
103
104(defcustom nxml-bind-meta-tab-to-complete-flag (not window-system)
105 "*Non-nil means bind M-TAB in `nxml-mode-map' to `nxml-complete'.
106C-return will be bound to `nxml-complete' in any case.
107M-TAB gets swallowed by many window systems/managers, and
108`documentation' will show M-TAB rather than C-return as the
109binding `rng-complete' when both are bound. So it's better
110to bind M-TAB only when it will work."
111 :group 'nxml
112 :set (lambda (sym flag)
113 (set-default sym flag)
114 (when (and (boundp 'nxml-mode-map) nxml-mode-map)
115 (define-key nxml-mode-map "\M-\t" (and flag 'nxml-complete))))
116 :type 'boolean)
117
118(defcustom nxml-prefer-utf-16-to-utf-8-flag nil
119 "*Non-nil means prefer UTF-16 to UTF-8 when saving a buffer.
120This is used only when a buffer does not contain an encoding declaration
121and when its current `buffer-file-coding-system' specifies neither UTF-16
122nor UTF-8."
123 :group 'nxml
124 :type 'boolean)
125
126(defcustom nxml-prefer-utf-16-little-to-big-endian-flag (eq system-type
127 'windows-nt)
128 "*Non-nil means prefer little-endian to big-endian byte-order for UTF-16.
129This is used only for saving a buffer; when reading the byte-order is
130auto-detected. It may be relevant both when there is no encoding declaration
131and when the encoding declaration specifies `UTF-16'."
132 :group 'nxml
133 :type 'boolean)
134
135(defcustom nxml-default-buffer-file-coding-system nil
136 "*Default value for `buffer-file-coding-system' for a buffer for a new file.
137Nil means use the default value of `buffer-file-coding-system' as normal.
138A buffer's `buffer-file-coding-system' affects what \\[nxml-insert-xml-declaration] inserts."
139 :group 'nxml
140 :type 'coding-system)
141
142(defcustom nxml-auto-insert-xml-declaration-flag nil
143 "*Non-nil means automatically insert an XML declaration in a new file.
144The XML declaration is inserted using `nxml-insert-xml-declaration'."
145 :group 'nxml
146 :type 'boolean)
147
92bbfd0d
JR
148(defface nxml-delimited-data
149 '((t (:inherit font-lock-doc-face)))
8cd39fb3 150 "Face used to highlight data enclosed between delimiters.
92bbfd0d
JR
151This is not used directly, but only via inheritance by other faces."
152 :group 'nxml-faces)
8cd39fb3 153
92bbfd0d
JR
154(defface nxml-name
155 '((t (:inherit font-lock-builtin-face)))
8cd39fb3
MH
156 "Face used to highlight various names.
157This includes element and attribute names, processing
158instruction targets and the CDATA keyword in a CDATA section.
159This is not used directly, but only via inheritance by other faces."
92bbfd0d 160 :group 'nxml-faces)
8cd39fb3 161
92bbfd0d
JR
162(defface nxml-ref
163 '((t (:inherit font-lock-constant-face)))
8cd39fb3
MH
164 "Face used to highlight character and entity references.
165This is not used directly, but only via inheritance by other faces."
92bbfd0d 166 :group 'nxml-faces)
8cd39fb3 167
92bbfd0d
JR
168(defface nxml-delimiter
169 nil
8cd39fb3
MH
170 "Face used to highlight delimiters.
171This is not used directly, but only via inheritance by other faces."
92bbfd0d 172 :group 'nxml-faces)
8cd39fb3 173
92bbfd0d 174(defface nxml-text
8cd39fb3
MH
175 nil
176 "Face used to highlight text."
92bbfd0d 177 :group 'nxml-faces)
8cd39fb3 178
92bbfd0d
JR
179(defface nxml-comment-content
180 '((t (:inherit font-lock-comment-face)))
8cd39fb3 181 "Face used to highlight the content of comments."
92bbfd0d 182 :group 'nxml-faces)
8cd39fb3 183
92bbfd0d
JR
184(defface nxml-comment-delimiter
185 '((t (:inherit font-lock-comment-delimiter-face)))
8cd39fb3 186 "Face used for the delimiters of comments, i.e <!-- and -->."
92bbfd0d 187 :group 'nxml-faces)
8cd39fb3 188
92bbfd0d
JR
189(defface nxml-processing-instruction-delimiter
190 '((t (:inherit nxml-delimiter)))
8cd39fb3 191 "Face used for the delimiters of processing instructions, i.e <? and ?>."
92bbfd0d 192 :group 'nxml-faces)
8cd39fb3 193
92bbfd0d
JR
194(defface nxml-processing-instruction-target
195 '((t (:inherit font-lock-keyword-face)))
8cd39fb3 196 "Face used for the target of processing instructions."
92bbfd0d 197 :group 'nxml-faces)
8cd39fb3 198
92bbfd0d
JR
199(defface nxml-processing-instruction-content
200 '((t (:inherit nxml-delimited-data)))
8cd39fb3 201 "Face used for the content of processing instructions."
92bbfd0d 202 :group 'nxml-faces)
8cd39fb3 203
92bbfd0d
JR
204(defface nxml-cdata-section-delimiter
205 '((t (:inherit nxml-delimiter)))
8cd39fb3 206 "Face used for the delimiters of CDATA sections, i.e <![, [, and ]]>."
92bbfd0d 207 :group 'nxml-faces)
8cd39fb3 208
92bbfd0d
JR
209(defface nxml-cdata-section-CDATA
210 '((t (:inherit nxml-name)))
8cd39fb3 211 "Face used for the CDATA keyword in CDATA sections."
92bbfd0d 212 :group 'nxml-faces)
8cd39fb3 213
92bbfd0d
JR
214(defface nxml-cdata-section-content
215 '((t (:inherit nxml-text)))
8cd39fb3 216 "Face used for the content of CDATA sections."
92bbfd0d 217 :group 'nxml-faces)
8cd39fb3 218
92bbfd0d
JR
219(defface nxml-char-ref-number
220 '((t (:inherit nxml-ref)))
8cd39fb3
MH
221 "Face used for the number in character references.
222This includes ths `x' in hex references."
92bbfd0d 223 :group 'nxml-faces)
8cd39fb3 224
92bbfd0d
JR
225(defface nxml-char-ref-delimiter
226 '((t (:inherit nxml-ref)))
8cd39fb3 227 "Face used for the delimiters of character references, i.e &# and ;."
92bbfd0d 228 :group 'nxml-faces)
8cd39fb3 229
92bbfd0d
JR
230(defface nxml-entity-ref-name
231 '((t (:inherit nxml-ref)))
8cd39fb3 232 "Face used for the entity name in general entity references."
92bbfd0d 233 :group 'nxml-faces)
8cd39fb3 234
92bbfd0d
JR
235(defface nxml-entity-ref-delimiter
236 '((t (:inherit nxml-ref)))
8cd39fb3 237 "Face used for the delimiters of entity references, i.e & and ;."
92bbfd0d 238 :group 'nxml-faces)
8cd39fb3 239
92bbfd0d
JR
240(defface nxml-tag-delimiter
241 '((t (:inherit nxml-delimiter)))
8cd39fb3 242 "Face used for the angle brackets delimiting tags.
92bbfd0d
JR
243`nxml-tag-slash' is used for slashes."
244 :group 'nxml-faces)
8cd39fb3 245
92bbfd0d
JR
246(defface nxml-tag-slash
247 '((t (:inherit nxml-tag-delimiter)))
8cd39fb3 248 "Face used for slashes in tags, both in end-tags and empty-elements."
92bbfd0d 249 :group 'nxml-faces)
8cd39fb3 250
92bbfd0d
JR
251(defface nxml-element-prefix
252 '((t (:inherit nxml-name)))
8cd39fb3 253 "Face used for the prefix of elements."
92bbfd0d 254 :group 'nxml-faces)
8cd39fb3 255
92bbfd0d
JR
256(defface nxml-element-colon
257 nil
8cd39fb3 258 "Face used for the colon in element names."
92bbfd0d 259 :group 'nxml-faces)
8cd39fb3 260
92bbfd0d
JR
261(defface nxml-element-local-name
262 '((t (:inherit font-lock-function-name-face)))
8cd39fb3 263 "Face used for the local name of elements."
92bbfd0d 264 :group 'nxml-faces)
8cd39fb3 265
92bbfd0d
JR
266(defface nxml-attribute-prefix
267 '((t (:inherit nxml-name)))
8cd39fb3 268 "Face used for the prefix of attributes."
92bbfd0d 269 :group 'nxml-faces)
8cd39fb3 270
92bbfd0d
JR
271(defface nxml-attribute-colon
272 '((t (:inherit nxml-delimiter)))
8cd39fb3 273 "Face used for the colon in attribute names."
92bbfd0d 274 :group 'nxml-faces)
8cd39fb3 275
92bbfd0d
JR
276(defface nxml-attribute-local-name
277 '((t (:inherit font-lock-variable-name-face)))
8cd39fb3 278 "Face used for the local name of attributes."
92bbfd0d 279 :group 'nxml-faces)
8cd39fb3 280
92bbfd0d
JR
281(defface nxml-namespace-attribute-xmlns
282 '((t (:inherit nxml-attribute-prefix)))
8cd39fb3 283 "Face used for `xmlns' in namespace attributes."
92bbfd0d 284 :group 'nxml-faces)
8cd39fb3 285
92bbfd0d
JR
286(defface nxml-namespace-attribute-colon
287 '((t (:inherit nxml-attribute-colon)))
8cd39fb3 288 "Face used for the colon in namespace attributes."
92bbfd0d 289 :group 'nxml-faces)
8cd39fb3 290
92bbfd0d
JR
291(defface nxml-namespace-attribute-prefix
292 '((t (:inherit nxml-attribute-local-name)))
8cd39fb3 293 "Face used for the prefix declared in namespace attributes."
92bbfd0d 294 :group 'nxml-faces)
8cd39fb3 295
92bbfd0d
JR
296(defface nxml-attribute-value
297 '((t (:inherit font-lock-string-face)))
8cd39fb3 298 "Face used for the value of attributes."
92bbfd0d 299 :group 'nxml-faces)
8cd39fb3 300
92bbfd0d
JR
301(defface nxml-attribute-value-delimiter
302 '((t (:inherit nxml-attribute-value)))
8cd39fb3 303 "Face used for the delimiters of attribute values."
92bbfd0d 304 :group 'nxml-faces)
8cd39fb3 305
92bbfd0d
JR
306(defface nxml-namespace-attribute-value
307 '((t (:inherit nxml-attribute-value)))
8cd39fb3 308 "Face used for the value of namespace attributes."
92bbfd0d 309 :group 'nxml-faces)
8cd39fb3 310
92bbfd0d
JR
311(defface nxml-namespace-attribute-value-delimiter
312 '((t (:inherit nxml-attribute-value-delimiter)))
8cd39fb3 313 "Face used for the delimiters of namespace attribute values."
92bbfd0d 314 :group 'nxml-faces)
8cd39fb3 315
92bbfd0d
JR
316(defface nxml-prolog-literal-delimiter
317 '((t (:inherit nxml-delimited-data)))
8cd39fb3 318 "Face used for the delimiters of literals in the prolog."
92bbfd0d 319 :group 'nxml-faces)
8cd39fb3 320
92bbfd0d
JR
321(defface nxml-prolog-literal-content
322 '((t (:inherit nxml-delimited-data)))
8cd39fb3 323 "Face used for the content of literals in the prolog."
92bbfd0d 324 :group 'nxml-faces)
8cd39fb3 325
92bbfd0d
JR
326(defface nxml-prolog-keyword
327 '((t (:inherit font-lock-keyword-face)))
8cd39fb3 328 "Face used for keywords in the prolog."
92bbfd0d 329 :group 'nxml-faces)
8cd39fb3 330
92bbfd0d
JR
331(defface nxml-markup-declaration-delimiter
332 '((t (:inherit nxml-delimiter)))
8cd39fb3
MH
333 "Face used for the delimiters of markup declarations in the prolog.
334The delimiters are <! and >."
92bbfd0d 335 :group 'nxml-faces)
8cd39fb3 336
92bbfd0d
JR
337(defface nxml-hash
338 '((t (:inherit nxml-name)))
8cd39fb3 339 "Face used for # before a name in the prolog."
92bbfd0d 340 :group 'nxml-faces)
8cd39fb3 341
92bbfd0d 342(defface nxml-glyph
8cd39fb3
MH
343 '((((type x))
344 (:family
345 "misc-fixed"
346 :background
347 "light grey"
348 :foreground
349 "black"
350 :weight
351 normal
352 :slant
353 normal))
354 (t
355 (:background
356 "light grey"
357 :foreground
358 "black"
359 :weight
360 normal
361 :slant
362 normal)))
363 "Face used for glyph for char references."
92bbfd0d 364 :group 'nxml-faces)
8cd39fb3
MH
365
366;;; Global variables
367
8cd39fb3
MH
368(defvar nxml-prolog-regions nil
369 "List of regions in the prolog to be fontified.
370See the function `xmltok-forward-prolog' for more information.")
371(make-variable-buffer-local 'nxml-prolog-regions)
372
373(defvar nxml-last-fontify-end nil
374 "Position where fontification last ended.
375Nil if the buffer changed since the last fontification.")
376(make-variable-buffer-local 'nxml-last-fontify-end)
377
378(defvar nxml-degraded nil
379 "Non-nil if currently operating in degraded mode.
380Degraded mode is enabled when an internal error is encountered in the
381fontification or after-change functions.")
382(make-variable-buffer-local 'nxml-degraded)
383
384(defvar nxml-completion-hook nil
385 "Hook run by `nxml-complete'.
386This hook is run until success.")
387
388(defvar nxml-in-mixed-content-hook nil
389 "Hook to determine whether point is in mixed content.
390The hook is called without arguments. It should return nil if it is
391definitely not mixed; non-nil otherwise. The hook will be run until
392one of the functions returns nil.")
393
394(defvar nxml-mixed-scan-distance 4000
395 "Maximum distance from point to scan when checking for mixed content.")
396
397(defvar nxml-end-tag-indent-scan-distance 4000
398 "Maximum distance from point to scan backwards when indenting end-tag.")
399
400(defvar nxml-char-ref-extra-display t
401 "Non-nil means display extra information for character references.
402The extra information consists of a tooltip with the character name
403and, if `nxml-char-ref-display-glyph-flag' is non-nil, a glyph
404corresponding to the referenced character following the character
405reference.")
406(make-variable-buffer-local 'nxml-char-ref-extra-display)
407
408(defvar nxml-mode-map
409 (let ((map (make-sparse-keymap)))
410 (define-key map "\M-\C-u" 'nxml-backward-up-element)
411 (define-key map "\M-\C-d" 'nxml-down-element)
412 (define-key map "\M-\C-n" 'nxml-forward-element)
413 (define-key map "\M-\C-p" 'nxml-backward-element)
414 (define-key map "\M-{" 'nxml-backward-paragraph)
415 (define-key map "\M-}" 'nxml-forward-paragraph)
416 (define-key map "\M-h" 'nxml-mark-paragraph)
417 (define-key map "\C-c\C-f" 'nxml-finish-element)
418 (define-key map "\C-c\C-m" 'nxml-split-element)
419 (define-key map "\C-c\C-b" 'nxml-balanced-close-start-tag-block)
420 (define-key map "\C-c\C-i" 'nxml-balanced-close-start-tag-inline)
421 (define-key map "\C-c\C-x" 'nxml-insert-xml-declaration)
422 (define-key map "\C-c\C-d" 'nxml-dynamic-markup-word)
423 ;; u is for Unicode
424 (define-key map "\C-c\C-u" 'nxml-insert-named-char)
425 (define-key map "\C-c\C-o" nxml-outline-prefix-map)
426 (define-key map [S-mouse-2] 'nxml-mouse-hide-direct-text-content)
427 (define-key map "/" 'nxml-electric-slash)
428 (define-key map [C-return] 'nxml-complete)
429 (when nxml-bind-meta-tab-to-complete-flag
430 (define-key map "\M-\t" 'nxml-complete))
431 map)
432 "Keymap for nxml-mode.")
433
434(defsubst nxml-set-face (start end face)
435 (when (and face (< start end))
436 (put-text-property start end 'face face)))
437
438(defun nxml-clear-face (start end)
439 (remove-text-properties start end '(face nil))
440 (nxml-clear-char-ref-extra-display start end))
441
442(defsubst nxml-set-fontified (start end)
443 (put-text-property start end 'fontified t))
444
445(defsubst nxml-clear-fontified (start end)
446 (remove-text-properties start end '(fontified nil)))
447
448;;;###autoload
449(defun nxml-mode ()
450 ;; We use C-c C-i instead of \\[nxml-balanced-close-start-tag-inline]
451 ;; because Emacs turns C-c C-i into C-c TAB which is hard to type and
452 ;; not mnemonic.
453 "Major mode for editing XML.
454
455Syntax highlighting is performed unless the variable
456`nxml-syntax-highlight-flag' is nil.
457
458\\[nxml-finish-element] finishes the current element by inserting an end-tag.
459C-c C-i closes a start-tag with `>' and then inserts a balancing end-tag
460leaving point between the start-tag and end-tag.
461\\[nxml-balanced-close-start-tag-block] is similar but for block rather than inline elements:
462the start-tag, point, and end-tag are all left on separate lines.
463If `nxml-slash-auto-complete-flag' is non-nil, then inserting a `</'
464automatically inserts the rest of the end-tag.
465
466\\[nxml-complete] performs completion on the symbol preceding point.
467
468\\[nxml-dynamic-markup-word] uses the contents of the current buffer
469to choose a tag to put around the word preceding point.
470
471Sections of the document can be displayed in outline form. The
472variable `nxml-section-element-name-regexp' controls when an element
473is recognized as a section. The same key sequences that change
474visibility in outline mode are used except that they start with C-c C-o
475instead of C-c.
476
477Validation is provided by the related minor-mode `rng-validate-mode'.
478This also makes completion schema- and context- sensitive. Element
479names, attribute names, attribute values and namespace URIs can all be
6bd1e223
JR
480completed. By default, `rng-validate-mode' is automatically enabled. You
481can toggle it using \\[rng-validate-mode] or change the default by
482customizing `rng-nxml-auto-validate-flag'.
8cd39fb3
MH
483
484\\[indent-for-tab-command] indents the current line appropriately.
485This can be customized using the variable `nxml-child-indent'
486and the variable `nxml-attribute-indent'.
487
488\\[nxml-insert-named-char] inserts a character reference using
489the character's name (by default, the Unicode name). \\[universal-argument] \\[nxml-insert-named-char]
490inserts the character directly.
491
492The Emacs commands that normally operate on balanced expressions will
493operate on XML markup items. Thus \\[forward-sexp] will move forward
494across one markup item; \\[backward-sexp] will move backward across
495one markup item; \\[kill-sexp] will kill the following markup item;
496\\[mark-sexp] will mark the following markup item. By default, each
497tag each treated as a single markup item; to make the complete element
498be treated as a single markup item, set the variable
499`nxml-sexp-element-flag' to t. For more details, see the function
500`nxml-forward-balanced-item'.
501
502\\[nxml-backward-up-element] and \\[nxml-down-element] move up and down the element structure.
503
504Many aspects this mode can be customized using
505\\[customize-group] nxml RET."
506 (interactive)
507 (kill-all-local-variables)
508 (setq major-mode 'nxml-mode)
509 (setq mode-name "nXML")
510 ;; We'll determine the fill prefix ourselves
511 (make-local-variable 'adaptive-fill-mode)
512 (setq adaptive-fill-mode nil)
513 (make-local-variable 'forward-sexp-function)
514 (setq forward-sexp-function 'nxml-forward-balanced-item)
515 (make-local-variable 'indent-line-function)
516 (setq indent-line-function 'nxml-indent-line)
517 (make-local-variable 'fill-paragraph-function)
518 (setq fill-paragraph-function 'nxml-do-fill-paragraph)
519 ;; Comment support
520 ;; This doesn't seem to work too well;
521 ;; I think we should probably roll our own nxml-comment-dwim function.
522 (make-local-variable 'comment-indent-function)
523 (setq comment-indent-function 'nxml-indent-line)
524 (make-local-variable 'comment-start)
525 (setq comment-start "<!--")
526 (make-local-variable 'comment-start-skip)
527 (setq comment-start-skip "<!--[ \t\r\n]*")
528 (make-local-variable 'comment-end)
529 (setq comment-end "-->")
530 (make-local-variable 'comment-end-skip)
531 (setq comment-end-skip "[ \t\r\n]*-->")
532 (make-local-variable 'comment-line-break-function)
533 (setq comment-line-break-function 'nxml-newline-and-indent)
534 (use-local-map nxml-mode-map)
535 (save-excursion
536 (save-restriction
537 (widen)
538 (nxml-clear-dependent-regions (point-min) (point-max))
539 (setq nxml-scan-end (copy-marker (point-min) nil))
540 (nxml-with-unmodifying-text-property-changes
541 (when nxml-syntax-highlight-flag
542 (nxml-clear-fontified (point-min) (point-max)))
543 (nxml-clear-inside (point-min) (point-max))
544 (nxml-with-invisible-motion
545 (nxml-scan-prolog)))))
546 (when nxml-syntax-highlight-flag
547 (add-hook 'fontification-functions 'nxml-fontify nil t))
548 (add-hook 'after-change-functions 'nxml-after-change nil t)
549 (add-hook 'write-contents-hooks 'nxml-prepare-to-save)
550 (when (not (and (buffer-file-name) (file-exists-p (buffer-file-name))))
551 (when (and nxml-default-buffer-file-coding-system
552 (not (local-variable-p 'buffer-file-coding-system)))
553 (setq buffer-file-coding-system nxml-default-buffer-file-coding-system))
554 (when nxml-auto-insert-xml-declaration-flag
555 (nxml-insert-xml-declaration)))
6bd1e223 556 (rng-nxml-mode-init)
b85b6604 557 (nxml-enable-unicode-char-name-sets)
8cd39fb3
MH
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.
654The 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.
883The buffer is assumed to be prepared for fontification.
884This does not set the fontified property, but it does clear
885faces 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.
897Leave 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
92bbfd0d
JR
912 '([nil 1 nxml-tag-delimiter]
913 [-1 nil nxml-tag-delimiter]
8cd39fb3
MH
914 (element-qname . 1)
915 attributes))
916
917(put 'partial-start-tag
918 'nxml-fontify-rule
92bbfd0d 919 '([nil 1 nxml-tag-delimiter]
8cd39fb3
MH
920 (element-qname . 1)
921 attributes))
922
923(put 'end-tag
924 'nxml-fontify-rule
92bbfd0d
JR
925 '([nil 1 nxml-tag-delimiter]
926 [1 2 nxml-tag-slash]
927 [-1 nil nxml-tag-delimiter]
8cd39fb3
MH
928 (element-qname . 2)))
929
930(put 'partial-end-tag
931 'nxml-fontify-rule
92bbfd0d
JR
932 '([nil 1 nxml-tag-delimiter]
933 [1 2 nxml-tag-slash]
8cd39fb3
MH
934 (element-qname . 2)))
935
936(put 'empty-element
937 'nxml-fontify-rule
92bbfd0d
JR
938 '([nil 1 nxml-tag-delimiter]
939 [-2 -1 nxml-tag-slash]
940 [-1 nil nxml-tag-delimiter]
8cd39fb3
MH
941 (element-qname . 1)
942 attributes))
943
944(put 'partial-empty-element
945 'nxml-fontify-rule
92bbfd0d
JR
946 '([nil 1 nxml-tag-delimiter]
947 [-1 nil nxml-tag-slash]
8cd39fb3
MH
948 (element-qname . 1)
949 attributes))
950
951(put 'char-ref
952 'nxml-fontify-rule
92bbfd0d
JR
953 '([nil 2 nxml-char-ref-delimiter]
954 [2 -1 nxml-char-ref-number]
955 [-1 nil nxml-char-ref-delimiter]
8cd39fb3
MH
956 char-ref))
957
958(put 'entity-ref
959 'nxml-fontify-rule
92bbfd0d
JR
960 '([nil 1 nxml-entity-ref-delimiter]
961 [1 -1 nxml-entity-ref-name]
962 [-1 nil nxml-entity-ref-delimiter]))
8cd39fb3
MH
963
964(put 'comment
965 'nxml-fontify-rule
92bbfd0d
JR
966 '([nil 4 nxml-comment-delimiter]
967 [4 -3 nxml-comment-content]
968 [-3 nil nxml-comment-delimiter]))
8cd39fb3
MH
969
970(put 'processing-instruction
971 'nxml-fontify-rule
92bbfd0d
JR
972 '([nil 2 nxml-processing-instruction-delimiter]
973 [-2 nil nxml-processing-instruction-delimiter]
8cd39fb3
MH
974 processing-instruction-content))
975
976(put 'cdata-section
977 'nxml-fontify-rule
92bbfd0d
JR
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]))
8cd39fb3
MH
983
984(put 'data
985 'nxml-fontify-rule
92bbfd0d 986 '([nil nil nxml-text]))
8cd39fb3
MH
987
988;; Prolog region types in list returned by xmltok-forward-prolog.
989
990(put 'xml-declaration
991 'nxml-fontify-rule
92bbfd0d
JR
992 '([nil 2 nxml-processing-instruction-delimiter]
993 [2 5 nxml-processing-instruction-target]
994 [-2 nil nxml-processing-instruction-delimiter]))
8cd39fb3
MH
995
996(put 'xml-declaration-attribute-name
997 'nxml-fontify-rule
92bbfd0d 998 '([nil nil nxml-attribute-local-name]))
8cd39fb3
MH
999
1000(put 'xml-declaration-attribute-value
1001 'nxml-fontify-rule
92bbfd0d
JR
1002 '([nil 1 nxml-attribute-value-delimiter]
1003 [1 -1 nxml-attribute-value]
1004 [-1 nil nxml-attribute-value-delimiter]))
8cd39fb3
MH
1005
1006(put 'processing-instruction-left
1007 'nxml-fontify-rule
92bbfd0d
JR
1008 '([nil 2 nxml-processing-instruction-delimiter]
1009 [2 nil nxml-processing-instruction-target]))
8cd39fb3
MH
1010
1011(put 'processing-instruction-right
1012 'nxml-fontify-rule
92bbfd0d
JR
1013 '([nil -2 nxml-processing-instruction-content]
1014 [-2 nil nxml-processing-instruction-delimiter]))
8cd39fb3
MH
1015
1016(put 'literal
1017 'nxml-fontify-rule
92bbfd0d
JR
1018 '([nil 1 nxml-prolog-literal-delimiter]
1019 [1 -1 nxml-prolog-literal-content]
1020 [-1 nil nxml-prolog-literal-delimiter]))
8cd39fb3
MH
1021
1022(put 'keyword
1023 'nxml-fontify-rule
92bbfd0d 1024 '([nil nil nxml-prolog-keyword]))
8cd39fb3
MH
1025
1026(put 'markup-declaration-open
1027 'nxml-fontify-rule
92bbfd0d
JR
1028 '([0 2 nxml-markup-declaration-delimiter]
1029 [2 nil nxml-prolog-keyword]))
8cd39fb3
MH
1030
1031(put 'markup-declaration-close
1032 'nxml-fontify-rule
92bbfd0d 1033 '([nil nil nxml-markup-declaration-delimiter]))
8cd39fb3
MH
1034
1035(put 'internal-subset-open
1036 'nxml-fontify-rule
92bbfd0d 1037 '([nil nil nxml-markup-declaration-delimiter]))
8cd39fb3
MH
1038
1039(put 'internal-subset-close
1040 'nxml-fontify-rule
92bbfd0d
JR
1041 '([nil 1 nxml-markup-declaration-delimiter]
1042 [-1 nil nxml-markup-declaration-delimiter]))
8cd39fb3
MH
1043
1044(put 'hash-name
1045 'nxml-fontify-rule
92bbfd0d
JR
1046 '([nil 1 nxml-hash]
1047 [1 nil nxml-prolog-keyword]))
8cd39fb3
MH
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
92bbfd0d
JR
1072 'nxml-element-prefix
1073 'nxml-element-colon
1074 'nxml-element-local-name)))
8cd39fb3
MH
1075 ((eq action 'attributes)
1076 (nxml-fontify-attributes))
1077 ((eq action 'processing-instruction-content)
1078 (nxml-set-face (+ start 2)
1079 xmltok-name-end
92bbfd0d 1080 'nxml-processing-instruction-target)
8cd39fb3
MH
1081 (nxml-set-face (save-excursion
1082 (goto-char xmltok-name-end)
1083 (skip-chars-forward " \t\r\n")
1084 (point))
1085 (- end 2)
92bbfd0d 1086 'nxml-processing-instruction-content))
8cd39fb3
MH
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)
92bbfd0d
JR
1109 'nxml-namespace-attribute-xmlns
1110 'nxml-namespace-attribute-colon
1111 'nxml-namespace-attribute-prefix
1112 'nxml-namespace-attribute-xmlns)
8cd39fb3
MH
1113 (nxml-fontify-qname (xmltok-attribute-name-start att)
1114 (xmltok-attribute-name-colon att)
1115 (xmltok-attribute-name-end att)
92bbfd0d
JR
1116 'nxml-attribute-prefix
1117 'nxml-attribute-colon
1118 'nxml-attribute-local-name))
8cd39fb3
MH
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
92bbfd0d
JR
1123 'nxml-namespace-attribute-value-delimiter
1124 'nxml-attribute-value-delimiter))
8cd39fb3 1125 (value-face (if namespace-declaration
92bbfd0d
JR
1126 'nxml-namespace-attribute-value
1127 'nxml-attribute-value)))
8cd39fb3
MH
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
1161With a prefix ARG, do nothing other than insert the slash.
1162
1163Otherwise, if `nxml-slash-auto-complete-flag' is non-nil, insert the
1164rest of the end-tag or empty-element if the slash is potentially part
1165of an end-tag or the close of an empty-element.
1166
1167If the slash is part of an end-tag that is the first non-whitespace
1168on 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.
1205Point is left between the start-tag and the end-tag.
1206If there is nothing but whitespace before the `<' that opens the
1207start-tag, then put point on a blank line, and put the end-tag on
1208another 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.
1214Point is left between the start-tag and the end-tag.
1215No 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.
1256Point is left after the newly inserted start-tag. When repeated,
1257split immediately before the previously inserted start-tag and leave
1258point 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.
1270The start-tag is inserted if STARTP is non-nil. Return the position
1271of 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.
1353When the line containing point ends with an end-tag and does not start
1354in the middle of a token, return the indent of the line containing the
1355matching start-tag, if there is one and it occurs at the beginning of
1356its 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'.
1446Return the merged indent context type. An indent context type is
1447either nil or one of the symbols start-tag, end-tag, markup, comment,
1448mixed."
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.
1467POS is the position of the first non-whitespace character of the line.
1468This 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.
1490Also for a line that starts inside an empty element.
1491POS is the position of the first non-whitespace character of the line.
1492This 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.
1524Otherwise return nil. START and END are the positions of the start
1525and end of the attribute value containing POS. This expects the
1526xmltok-* 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.
1543OPEN-DELIM and CLOSE-DELIM are strings giving the opening and closing
1544delimiters. POS is the position of the first non-whitespace character
1545of 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
1568Inserts as many characters as can be completed. However, if not even
1569one character can be completed, then a buffer with the possibilities
1570is popped up and the symbol is read from the minibuffer with
1571completion. If the symbol is complete, then any characters that must
1572follow the symbol are also inserted.
1573
1574The name space used for completion and what is treated as a symbol
1575depends on the context. The contexts in which completion is performed
1576depend 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.
1587With ARG, do it that many times. Negative arg -N means
1588move backward across N balanced expressions.
1589This is the equivalent of `forward-sexp' for XML.
1590
1591An element contains as items strings with no markup, tags, processing
1592instructions, comments, CDATA sections, entity references and
1593characters references. However, if the variable
1594`nxml-sexp-element-flag' is non-nil, then an element is treated as a
1595single markup item. A start-tag contains an element name followed by
1596one or more attributes. An end-tag contains just an element name. An
1597attribute value literals contains strings with no markup, entity
1598references and character references. A processing instruction
1599consists of a target and a content string. A comment or a CDATA
1600section contains a single string. An entity reference contains a
1601single 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.
1872With ARG, do this that many times.
1873Negative 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.
1918With ARG, do it that many times.
1919Negative 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.
1937With ARG, do it that many times.
1938Negative 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.
1967The 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.
1997Return 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.
2013Return 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.
2062Return nil if the current token does not contain the paragraph end.
2063Only characters after OFFSET from the start of the token are eligible.
2064HAD-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.
2118Return nil if the current token does not contain the paragraph start.
2119Only characters before OFFSET from the end of the token are eligible.
2120HAD-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.
2189Point must be after an end-tag or before a start-tag.
2190ENDP 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.
2317This attempts to find a tag to put around the word before point based
2318on the contents of the current buffer. The end-tag will be inserted at
2319point. The start-tag will be inserted at or before the beginning of
2320the word before point; the contents of the current buffer is used to
2321decide where.
2322
2323It works in a similar way to \\[dabbrev-expand]. It searches first
2324backwards from point, then forwards from point for an element whose
2325content is a string which matches the contents of the buffer before
2326point and which includes at least the word before point. It then
2327copies the start- and end-tags from that element and uses them to
2328surround the matching string before point.
2329
2330Repeating \\[nxml-dynamic-markup-word] immediately after successful
2331\\[nxml-dynamic-markup-word] removes the previously inserted markup
2332and 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
b85b6604 2436(defvar nxml-char-name-ignore-case t)
8cd39fb3
MH
2437
2438(defvar nxml-char-name-alist nil
2439 "Alist of character names.
2440Each member of the list has the form (NAME CODE . NAMESET),
2441where NAME is a string naming a character, NAMESET is a symbol
2442identifying a set of names and CODE is an integer specifying the
2443Unicode scalar value of the named character.
2444The NAME will only be used for completion if NAMESET has
2445a non-nil `nxml-char-name-set-enabled' property.
2446If NAMESET does does not have `nxml-char-name-set-defined' property,
2447then it must have a `nxml-char-name-set-file' property and `load'
2448will be applied to the value of this property if the nameset
2449is enabled.")
2450
2451(defvar nxml-char-name-table (make-hash-table :test 'eq)
2452 "Hash table for mapping char codes to names.
2453Each key is a Unicode scalar value.
2454Each value is a list of pairs of the form (NAMESET . NAME),
2455where NAMESET is a symbol identifying a set of names,
2456and 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.
2478NAMESET is a symbol identifying the set.
2479Alist is a list where each member has the form (NAME CODE),
2480where NAME is a string naming a character and code
2481is 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)
e290ff07 2500 (mapc 'nxml-maybe-load-char-name-set nxml-autoload-char-name-set-list)
8cd39fb3
MH
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.
2513The name is read from the minibuffer.
2514Normally, inserts the character as a numeric character reference.
2515With a prefix argument, inserts the character directly."
2516 (interactive "*P")
e290ff07 2517 (mapc 'nxml-maybe-load-char-name-set nxml-autoload-char-name-set-list)
8cd39fb3
MH
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
92bbfd0d 2569 (nxml-glyph-display-string n 'nxml-glyph)))
8cd39fb3
MH
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
92bbfd0d 2579 (propertize glyph-string 'face 'nxml-glyph)))))))
8cd39fb3
MH
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
8cd39fb3
MH
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
ab4c34c6 2626;; arch-tag: 8603bc5f-1ef9-4021-b223-322fb2ca708e
8cd39fb3 2627;;; nxml-mode.el ends here