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