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