nxml-mode.el 86.8 KB
Newer Older
1
;;; nxml-mode.el --- a new XML mode  -*- lexical-binding:t -*-
Mark A. Hershberger's avatar
Mark A. Hershberger committed
2

3
;; Copyright (C) 2003-2004, 2007-2014 Free Software Foundation, Inc.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
4 5

;; Author: James Clark
6
;; Keywords: wp, hypermedia, languages, XML
Mark A. Hershberger's avatar
Mark A. Hershberger committed
7

Glenn Morris's avatar
Glenn Morris committed
8
;; This file is part of GNU Emacs.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
9

10
;; GNU Emacs is free software: you can redistribute it and/or modify
Glenn Morris's avatar
Glenn Morris committed
11
;; it under the terms of the GNU General Public License as published by
12 13
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
14

Glenn Morris's avatar
Glenn Morris committed
15 16 17 18 19 20
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
21
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
22 23 24 25 26 27 28 29 30 31

;;; Commentary:

;; See nxml-rap.el for description of parsing strategy.

;;; Code:

(when (featurep 'mucs)
  (error "nxml-mode is not compatible with Mule-UCS"))

32
(eval-when-compile (require 'cl-lib))
Glenn Morris's avatar
Glenn Morris committed
33

Mark A. Hershberger's avatar
Mark A. Hershberger committed
34 35 36 37 38 39
(require 'xmltok)
(require 'nxml-enc)
(require 'nxml-glyph)
(require 'nxml-util)
(require 'nxml-rap)
(require 'nxml-outln)
40 41 42 43
;; nxml-mode calls rng-nxml-mode-init, which is autoloaded from rng-nxml.
;; So we might as well just require it and silence the compiler.
(provide 'nxml-mode)			; avoid recursive require
(require 'rng-nxml)
44

Mark A. Hershberger's avatar
Mark A. Hershberger committed
45 46 47
;;; Customization

(defgroup nxml nil
48
  "New XML editing mode."
49
  :link '(custom-manual "(nxml-mode) Top")
Chong Yidong's avatar
Chong Yidong committed
50
  :group 'languages)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
51

52
(defgroup nxml-faces nil
Mark A. Hershberger's avatar
Mark A. Hershberger committed
53
  "Faces for XML syntax highlighting."
Chong Yidong's avatar
Chong Yidong committed
54
  :group 'nxml)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
55 56

(defcustom nxml-char-ref-display-glyph-flag t
57
  "Non-nil means display glyph following character reference.
58 59 60
The glyph is displayed in face `nxml-glyph'.  The abnormal hook
`nxml-glyph-set-functions' can be used to change the characters
for which glyphs are displayed."
Mark A. Hershberger's avatar
Mark A. Hershberger committed
61 62 63 64
  :group 'nxml
  :type 'boolean)

(defcustom nxml-sexp-element-flag nil
65
  "Non-nil means sexp commands treat an element as a single expression."
Mark A. Hershberger's avatar
Mark A. Hershberger committed
66 67 68 69
  :group 'nxml
  :type 'boolean)

(defcustom nxml-slash-auto-complete-flag nil
70
  "Non-nil means typing a slash automatically completes the end-tag.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
71 72 73 74 75
This is used by `nxml-electric-slash'."
  :group 'nxml
  :type 'boolean)

(defcustom nxml-child-indent 2
76
  "Indentation for the children of an element relative to the start-tag.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
77 78 79 80 81 82
This only applies when the line or lines containing the start-tag contains
nothing else other than that start-tag."
  :group 'nxml
  :type 'integer)

(defcustom nxml-attribute-indent 4
83
  "Indentation for the attributes of an element relative to the start-tag.
84 85 86
This only applies when the first attribute of a tag starts a line.
In other cases, the first attribute on one line is indented the same
as the first attribute on the previous line."
Mark A. Hershberger's avatar
Mark A. Hershberger committed
87 88 89
  :group 'nxml
  :type 'integer)

90 91
(defcustom nxml-bind-meta-tab-to-complete-flag t
  "Non-nil means to use nXML completion in \\[completion-at-point]."
Mark A. Hershberger's avatar
Mark A. Hershberger committed
92 93 94 95
  :group 'nxml
  :type 'boolean)

(defcustom nxml-prefer-utf-16-to-utf-8-flag nil
96
  "Non-nil means prefer UTF-16 to UTF-8 when saving a buffer.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
97 98 99 100 101 102 103 104
This is used only when a buffer does not contain an encoding declaration
and when its current `buffer-file-coding-system' specifies neither UTF-16
nor UTF-8."
  :group 'nxml
  :type 'boolean)

(defcustom nxml-prefer-utf-16-little-to-big-endian-flag (eq system-type
							    'windows-nt)
105
  "Non-nil means prefer little-endian to big-endian byte-order for UTF-16.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
106 107 108 109 110 111 112
This is used only for saving a buffer; when reading the byte-order is
auto-detected. It may be relevant both when there is no encoding declaration
and when the encoding declaration specifies `UTF-16'."
  :group 'nxml
  :type 'boolean)

(defcustom nxml-default-buffer-file-coding-system nil
113
  "Default value for `buffer-file-coding-system' for a buffer for a new file.
114
A value of nil means use the default value of `buffer-file-coding-system' as normal.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
115 116 117 118 119
A buffer's `buffer-file-coding-system' affects what \\[nxml-insert-xml-declaration] inserts."
  :group 'nxml
  :type 'coding-system)

(defcustom nxml-auto-insert-xml-declaration-flag nil
120
  "Non-nil means automatically insert an XML declaration in a new file.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
121 122 123 124
The XML declaration is inserted using `nxml-insert-xml-declaration'."
  :group 'nxml
  :type 'boolean)

125 126
(defface nxml-delimited-data
  '((t (:inherit font-lock-doc-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
127
  "Face used to highlight data enclosed between delimiters.
128 129
This is not used directly, but only via inheritance by other faces."
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
130

131 132
(defface nxml-name
  '((t (:inherit font-lock-builtin-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
133 134 135 136
  "Face used to highlight various names.
This includes element and attribute names, processing
instruction targets and the CDATA keyword in a CDATA section.
This is not used directly, but only via inheritance by other faces."
137
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
138

139 140
(defface nxml-ref
  '((t (:inherit font-lock-constant-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
141 142
  "Face used to highlight character and entity references.
This is not used directly, but only via inheritance by other faces."
143
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
144

145 146
(defface nxml-delimiter
  nil
Mark A. Hershberger's avatar
Mark A. Hershberger committed
147 148
  "Face used to highlight delimiters.
This is not used directly, but only via inheritance by other faces."
149
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
150

151
(defface nxml-text
Mark A. Hershberger's avatar
Mark A. Hershberger committed
152 153
  nil
  "Face used to highlight text."
154
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
155

156 157
(defface nxml-comment-content
  '((t (:inherit font-lock-comment-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
158
  "Face used to highlight the content of comments."
159
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
160

161 162
(defface nxml-comment-delimiter
  '((t (:inherit font-lock-comment-delimiter-face)))
163
  "Face used for the delimiters of comments, i.e., <!-- and -->."
164
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
165

166 167
(defface nxml-processing-instruction-delimiter
  '((t (:inherit nxml-delimiter)))
168
  "Face used for the delimiters of processing instructions, i.e., <? and ?>."
169
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
170

171 172
(defface nxml-processing-instruction-target
  '((t (:inherit font-lock-keyword-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
173
  "Face used for the target of processing instructions."
174
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
175

176 177
(defface nxml-processing-instruction-content
  '((t (:inherit nxml-delimited-data)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
178
  "Face used for the content of processing instructions."
179
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
180

181 182
(defface nxml-cdata-section-delimiter
  '((t (:inherit nxml-delimiter)))
183
  "Face used for the delimiters of CDATA sections, i.e., <![, [, and ]]>."
184
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
185

186 187
(defface nxml-cdata-section-CDATA
  '((t (:inherit nxml-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
188
  "Face used for the CDATA keyword in CDATA sections."
189
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
190

191 192
(defface nxml-cdata-section-content
  '((t (:inherit nxml-text)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
193
  "Face used for the content of CDATA sections."
194
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
195

196 197
(defface nxml-char-ref-number
  '((t (:inherit nxml-ref)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
198 199
  "Face used for the number in character references.
This includes ths `x' in hex references."
200
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
201

202 203
(defface nxml-char-ref-delimiter
  '((t (:inherit nxml-ref)))
204
  "Face used for the delimiters of character references, i.e., &# and ;."
205
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
206

207 208
(defface nxml-entity-ref-name
  '((t (:inherit nxml-ref)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
209
  "Face used for the entity name in general entity references."
210
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
211

212 213
(defface nxml-entity-ref-delimiter
  '((t (:inherit nxml-ref)))
214
  "Face used for the delimiters of entity references, i.e., & and ;."
215
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
216

217 218
(defface nxml-tag-delimiter
  '((t (:inherit nxml-delimiter)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
219
  "Face used for the angle brackets delimiting tags.
220 221
`nxml-tag-slash' is used for slashes."
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
222

223 224
(defface nxml-tag-slash
  '((t (:inherit nxml-tag-delimiter)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
225
  "Face used for slashes in tags, both in end-tags and empty-elements."
226
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
227

228 229
(defface nxml-element-prefix
  '((t (:inherit nxml-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
230
  "Face used for the prefix of elements."
231
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
232

233 234
(defface nxml-element-colon
  nil
Mark A. Hershberger's avatar
Mark A. Hershberger committed
235
  "Face used for the colon in element names."
236
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
237

238 239
(defface nxml-element-local-name
  '((t (:inherit font-lock-function-name-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
240
  "Face used for the local name of elements."
241
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
242

243 244
(defface nxml-attribute-prefix
  '((t (:inherit nxml-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
245
  "Face used for the prefix of attributes."
246
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
247

248 249
(defface nxml-attribute-colon
  '((t (:inherit nxml-delimiter)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
250
  "Face used for the colon in attribute names."
251
  :group 'nxml-faces)
252

253 254
(defface nxml-attribute-local-name
  '((t (:inherit font-lock-variable-name-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
255
  "Face used for the local name of attributes."
256
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
257

258 259
(defface nxml-namespace-attribute-xmlns
  '((t (:inherit nxml-attribute-prefix)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
260
  "Face used for `xmlns' in namespace attributes."
261
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
262

263 264
(defface nxml-namespace-attribute-colon
  '((t (:inherit nxml-attribute-colon)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
265
  "Face used for the colon in namespace attributes."
266
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
267

268 269
(defface nxml-namespace-attribute-prefix
  '((t (:inherit nxml-attribute-local-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
270
  "Face used for the prefix declared in namespace attributes."
271
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
272

273 274
(defface nxml-attribute-value
  '((t (:inherit font-lock-string-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
275
  "Face used for the value of attributes."
276
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
277

278 279
(defface nxml-attribute-value-delimiter
  '((t (:inherit nxml-attribute-value)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
280
  "Face used for the delimiters of attribute values."
281
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
282

283 284
(defface nxml-namespace-attribute-value
  '((t (:inherit nxml-attribute-value)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
285
  "Face used for the value of namespace attributes."
286
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
287

288 289
(defface nxml-namespace-attribute-value-delimiter
  '((t (:inherit nxml-attribute-value-delimiter)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
290
  "Face used for the delimiters of namespace attribute values."
291
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
292

293 294
(defface nxml-prolog-literal-delimiter
  '((t (:inherit nxml-delimited-data)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
295
  "Face used for the delimiters of literals in the prolog."
296
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
297

298 299
(defface nxml-prolog-literal-content
  '((t (:inherit nxml-delimited-data)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
300
  "Face used for the content of literals in the prolog."
301
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
302

303 304
(defface nxml-prolog-keyword
  '((t (:inherit font-lock-keyword-face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
305
  "Face used for keywords in the prolog."
306
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
307

308 309
(defface nxml-markup-declaration-delimiter
  '((t (:inherit nxml-delimiter)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
310 311
  "Face used for the delimiters of markup declarations in the prolog.
The delimiters are <! and >."
312
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
313

314 315
(defface nxml-hash
  '((t (:inherit nxml-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
316
  "Face used for # before a name in the prolog."
317
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
318

319
(defface nxml-glyph
Mark A. Hershberger's avatar
Mark A. Hershberger committed
320 321 322 323 324 325 326 327
  '((((type x))
     (:family
      "misc-fixed"
      :background
      "light grey"
      :foreground
      "black"
      :weight
328
      normal
Mark A. Hershberger's avatar
Mark A. Hershberger committed
329 330 331 332 333 334 335 336
      :slant
      normal))
    (t
     (:background
      "light grey"
      :foreground
      "black"
      :weight
337
      normal
Mark A. Hershberger's avatar
Mark A. Hershberger committed
338 339 340
      :slant
      normal)))
  "Face used for glyph for char references."
341
  :group 'nxml-faces)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
342 343 344

;;; Global variables

345 346 347 348 349 350
(defvar nxml-parent-document nil
  "The parent document for a part of a modular document.
Use `nxml-parent-document-set' to set it.")
(make-variable-buffer-local 'nxml-parent-document)
(put 'nxml-parent-document 'safe-local-variable 'stringp)

Mark A. Hershberger's avatar
Mark A. Hershberger committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
(defvar nxml-prolog-regions nil
  "List of regions in the prolog to be fontified.
See the function `xmltok-forward-prolog' for more information.")
(make-variable-buffer-local 'nxml-prolog-regions)

(defvar nxml-degraded nil
  "Non-nil if currently operating in degraded mode.
Degraded mode is enabled when an internal error is encountered in the
fontification or after-change functions.")
(make-variable-buffer-local 'nxml-degraded)

(defvar nxml-completion-hook nil
  "Hook run by `nxml-complete'.
This hook is run until success.")

(defvar nxml-in-mixed-content-hook nil
  "Hook to determine whether point is in mixed content.
The hook is called without arguments.  It should return nil if it is
definitely not mixed; non-nil otherwise.  The hook will be run until
one of the functions returns nil.")

(defvar nxml-mixed-scan-distance 4000
  "Maximum distance from point to scan when checking for mixed content.")

(defvar nxml-end-tag-indent-scan-distance 4000
  "Maximum distance from point to scan backwards when indenting end-tag.")

(defvar nxml-char-ref-extra-display t
  "Non-nil means display extra information for character references.
The extra information consists of a tooltip with the character name
and, if `nxml-char-ref-display-glyph-flag' is non-nil, a glyph
corresponding to the referenced character following the character
reference.")
(make-variable-buffer-local 'nxml-char-ref-extra-display)

(defvar nxml-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\M-\C-u" 'nxml-backward-up-element)
    (define-key map "\M-\C-d" 'nxml-down-element)
    (define-key map "\M-\C-n" 'nxml-forward-element)
    (define-key map "\M-\C-p" 'nxml-backward-element)
    (define-key map "\M-{" 'nxml-backward-paragraph)
    (define-key map "\M-}" 'nxml-forward-paragraph)
    (define-key map "\M-h" 'nxml-mark-paragraph)
    (define-key map "\C-c\C-f" 'nxml-finish-element)
396
    (define-key map "\C-c]" 'nxml-finish-element)
397
    (define-key map "\C-c/" 'nxml-finish-element)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
398 399 400 401 402 403 404 405 406 407
    (define-key map "\C-c\C-m" 'nxml-split-element)
    (define-key map "\C-c\C-b" 'nxml-balanced-close-start-tag-block)
    (define-key map "\C-c\C-i" 'nxml-balanced-close-start-tag-inline)
    (define-key map "\C-c\C-x" 'nxml-insert-xml-declaration)
    (define-key map "\C-c\C-d" 'nxml-dynamic-markup-word)
    ;; u is for Unicode
    (define-key map "\C-c\C-u" 'nxml-insert-named-char)
    (define-key map "\C-c\C-o" nxml-outline-prefix-map)
    (define-key map [S-mouse-2] 'nxml-mouse-hide-direct-text-content)
    (define-key map "/" 'nxml-electric-slash)
408
    (define-key map "\M-\t" 'completion-at-point)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
409 410 411
    map)
  "Keymap for nxml-mode.")

Michael Olson's avatar
Michael Olson committed
412 413 414 415
(defvar nxml-font-lock-keywords
  '(nxml-fontify-matcher)
  "Default font lock keywords for nxml-mode.")

Mark A. Hershberger's avatar
Mark A. Hershberger committed
416 417
(defsubst nxml-set-face (start end face)
  (when (and face (< start end))
Michael Olson's avatar
Michael Olson committed
418
    (font-lock-append-text-property start end 'face face)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
(defun nxml-parent-document-set (parent-document)
  "Set `nxml-parent-document' and inherit the DTD &c."
  ;; FIXME: this does not work.
  ;;  the idea is that by inheriting some variables from the parent,
  ;;  `rng-validate-mode' will validate entities declared in the parent.
  ;;  alas, the most interesting variables (`rng-compile-table' et al)
  ;;  are circular and cannot be printed even with `print-circle'.
  (interactive "fParent document")
  (let (dtd current-schema current-schema-file-name compile-table
        ipattern-table last-ipattern-index)
    (when (string= (file-truename parent-document)
                   (file-truename buffer-file-name))
      (error "Parent document cannot be the same as the document"))
    (with-current-buffer (find-file-noselect parent-document)
      (setq dtd rng-dtd
            current-schema rng-current-schema
            current-schema-file-name rng-current-schema-file-name
            compile-table rng-compile-table
            ipattern-table rng-ipattern-table
            last-ipattern-index rng-last-ipattern-index
            parent-document buffer-file-name))
    (setq rng-dtd dtd
          rng-current-schema current-schema
          rng-current-schema-file-name current-schema-file-name
          rng-compile-table compile-table
          rng-ipattern-table ipattern-table
          rng-last-ipattern-index last-ipattern-index
          nxml-parent-document parent-document)
    (message "Set parent document to %s" parent-document)
    (when rng-validate-mode
      (rng-validate-while-idle (current-buffer)))))

Mark A. Hershberger's avatar
Mark A. Hershberger committed
452
;;;###autoload
453
(define-derived-mode nxml-mode text-mode "nXML"
Mark A. Hershberger's avatar
Mark A. Hershberger committed
454 455 456 457 458 459 460
  ;; We use C-c C-i instead of \\[nxml-balanced-close-start-tag-inline]
  ;; because Emacs turns C-c C-i into C-c TAB which is hard to type and
  ;; not mnemonic.
  "Major mode for editing XML.

\\[nxml-finish-element] finishes the current element by inserting an end-tag.
C-c C-i closes a start-tag with `>' and then inserts a balancing end-tag
461
leaving point between the start-tag and end-tag.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
462 463 464 465 466
\\[nxml-balanced-close-start-tag-block] is similar but for block rather than inline elements:
the start-tag, point, and end-tag are all left on separate lines.
If `nxml-slash-auto-complete-flag' is non-nil, then inserting a `</'
automatically inserts the rest of the end-tag.

467
\\[completion-at-point] performs completion on the symbol preceding point.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
468 469 470 471 472 473 474 475 476 477 478 479 480

\\[nxml-dynamic-markup-word] uses the contents of the current buffer
to choose a tag to put around the word preceding point.

Sections of the document can be displayed in outline form.  The
variable `nxml-section-element-name-regexp' controls when an element
is recognized as a section.  The same key sequences that change
visibility in outline mode are used except that they start with C-c C-o
instead of C-c.

Validation is provided by the related minor-mode `rng-validate-mode'.
This also makes completion schema- and context- sensitive.  Element
names, attribute names, attribute values and namespace URIs can all be
481 482
completed. By default, `rng-validate-mode' is automatically enabled.
You can toggle it using \\[rng-validate-mode] or change the default by
483
customizing `rng-nxml-auto-validate-flag'.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
484 485 486 487 488 489

\\[indent-for-tab-command] indents the current line appropriately.
This can be customized using the variable `nxml-child-indent'
and the variable `nxml-attribute-indent'.

\\[nxml-insert-named-char] inserts a character reference using
490 491
the character's name (by default, the Unicode name).
\\[universal-argument] \\[nxml-insert-named-char] inserts the character directly.
Mark A. Hershberger's avatar
Mark A. Hershberger committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506

The Emacs commands that normally operate on balanced expressions will
operate on XML markup items.  Thus \\[forward-sexp] will move forward
across one markup item; \\[backward-sexp] will move backward across
one markup item; \\[kill-sexp] will kill the following markup item;
\\[mark-sexp] will mark the following markup item.  By default, each
tag each treated as a single markup item; to make the complete element
be treated as a single markup item, set the variable
`nxml-sexp-element-flag' to t.  For more details, see the function
`nxml-forward-balanced-item'.

\\[nxml-backward-up-element] and \\[nxml-down-element] move up and down the element structure.

Many aspects this mode can be customized using
\\[customize-group] nxml RET."
507
  ;; (kill-all-local-variables)
508
  (set (make-local-variable 'mode-line-process) '((nxml-degraded "/degraded")))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
  ;; We'll determine the fill prefix ourselves
  (make-local-variable 'adaptive-fill-mode)
  (setq adaptive-fill-mode nil)
  (make-local-variable 'forward-sexp-function)
  (setq forward-sexp-function 'nxml-forward-balanced-item)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'nxml-indent-line)
  (make-local-variable 'fill-paragraph-function)
  (setq fill-paragraph-function 'nxml-do-fill-paragraph)
  ;; Comment support
  ;; This doesn't seem to work too well;
  ;; I think we should probably roll our own nxml-comment-dwim function.
  (make-local-variable 'comment-indent-function)
  (setq comment-indent-function 'nxml-indent-line)
  (make-local-variable 'comment-start)
  (setq comment-start "<!--")
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "<!--[ \t\r\n]*")
  (make-local-variable 'comment-end)
  (setq comment-end "-->")
  (make-local-variable 'comment-end-skip)
  (setq comment-end-skip "[ \t\r\n]*-->")
  (make-local-variable 'comment-line-break-function)
  (setq comment-line-break-function 'nxml-newline-and-indent)
  (use-local-map nxml-mode-map)
  (save-excursion
    (save-restriction
      (widen)
      (setq nxml-scan-end (copy-marker (point-min) nil))
538
      (with-silent-modifications
Michael Olson's avatar
Michael Olson committed
539
        (nxml-clear-inside (point-min) (point-max))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
540 541
	(nxml-with-invisible-motion
	  (nxml-scan-prolog)))))
542 543
  (add-hook 'completion-at-point-functions
            #'nxml-completion-at-point-function nil t)
544
  (setq-local syntax-propertize-function #'nxml-after-change)
545 546
  (add-hook 'change-major-mode-hook 'nxml-cleanup nil t)

547 548 549 550
  ;; Emacs 23 handles the encoding attribute on the xml declaration
  ;; transparently to nxml-mode, so there is no longer a need for the below
  ;; hook. The hook also had the drawback of overriding explicit user
  ;; instruction to save as some encoding other than utf-8.
551
  ;;(add-hook 'write-contents-hooks 'nxml-prepare-to-save)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
552 553 554 555 556 557
  (when (not (and (buffer-file-name) (file-exists-p (buffer-file-name))))
    (when (and nxml-default-buffer-file-coding-system
	       (not (local-variable-p 'buffer-file-coding-system)))
      (setq buffer-file-coding-system nxml-default-buffer-file-coding-system))
    (when nxml-auto-insert-xml-declaration-flag
      (nxml-insert-xml-declaration)))
Michael Olson's avatar
Michael Olson committed
558 559 560 561 562 563 564 565 566 567 568

  (setq font-lock-defaults
        '(nxml-font-lock-keywords
          t    ; keywords-only; we highlight comments and strings here
          nil  ; font-lock-keywords-case-fold-search. XML is case sensitive
          nil  ; no special syntax table
          nil  ; no automatic syntactic fontification
          (font-lock-extend-region-functions . (nxml-extend-region))
          (jit-lock-contextually . t)
          (font-lock-unfontify-region-function . nxml-unfontify-region)))

569
  (rng-nxml-mode-init)
570
  (nxml-enable-unicode-char-name-sets))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
571

572 573 574 575 576 577 578
(defun nxml-cleanup ()
  "Clean up after nxml-mode."
  ;; Disable associated minor modes.
  (rng-validate-mode -1)
  ;; Clean up fontification.
  (save-excursion
    (widen)
579
    (with-silent-modifications
580
      (nxml-with-invisible-motion
581
       (remove-text-properties (point-min) (point-max) '(face)))))
582 583
  (remove-hook 'change-major-mode-hook 'nxml-cleanup t))

Mark A. Hershberger's avatar
Mark A. Hershberger committed
584 585 586 587 588 589 590 591 592 593
(defun nxml-degrade (context err)
  (message "Internal nXML mode error in %s (%s), degrading"
	   context
	   (error-message-string err))
  (ding)
  (setq nxml-degraded t)
  (setq nxml-prolog-end 1)
  (save-excursion
    (save-restriction
      (widen)
594
      (with-silent-modifications
595
	(nxml-clear-inside (point-min) (point-max))))))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
596 597 598

;;; Change management

599
(defvar font-lock-beg) (defvar font-lock-end)
Michael Olson's avatar
Michael Olson committed
600 601 602 603 604 605 606 607
(defun nxml-debug-region (start end)
  (interactive "r")
  (let ((font-lock-beg start)
        (font-lock-end end))
    (nxml-extend-region)
    (goto-char font-lock-beg)
    (set-mark font-lock-end)))

608 609 610
(defun nxml-after-change (start end)
  ;; Called via syntax-propertize-function.
  (unless nxml-degraded
Michael Olson's avatar
Michael Olson committed
611
    (nxml-with-degradation-on-error 'nxml-after-change
612 613 614 615 616 617
      (save-restriction
        (widen)
        (nxml-with-invisible-motion
         (nxml-after-change1 start end))))))

(defun nxml-after-change1 (start end)
618 619 620 621
  "After-change bookkeeping.
Returns a cons cell containing a possibly-enlarged change region.
You must call `nxml-extend-region' on this expanded region to obtain
the full extent of the area needing refontification.
Michael Olson's avatar
Michael Olson committed
622 623 624

For bookkeeping, call this function even when fontification is
disabled."
625 626 627 628 629 630 631 632
  ;; If the prolog might have changed, rescan the prolog.
  (when (<= start
            ;; Add 2 so as to include the < and following char that
            ;; start the instance (document element), since changing
            ;; these can change where the prolog ends.
            (+ nxml-prolog-end 2))
    (nxml-scan-prolog)
    (setq start (point-min)))
Michael Olson's avatar
Michael Olson committed
633 634 635 636 637 638 639 640

  (when (> end nxml-prolog-end)
    (goto-char start)
    (nxml-move-tag-backwards (point-min))
    (setq start (point))
    (setq end (max (nxml-scan-after-change start end)
                   end)))

641
  (nxml-debug-change "nxml-after-change1" start end))
Michael Olson's avatar
Michael Olson committed
642

Mark A. Hershberger's avatar
Mark A. Hershberger committed
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
;;; Encodings

(defun nxml-insert-xml-declaration ()
  "Insert an XML declaration at the beginning of buffer.
The XML declaration will declare an encoding depending on the buffer's
`buffer-file-coding-system'."
  (interactive "*")
  (let ((coding-system
	 (if (and buffer-file-coding-system
		  (coding-system-p buffer-file-coding-system)
		  (coding-system-get buffer-file-coding-system
				     'mime-charset))
	     buffer-file-coding-system
	   (nxml-choose-utf-coding-system))))
    (goto-char (point-min))
    (insert (format "<?xml version=\"1.0\" encoding=\"%s\"?>\n"
		    (nxml-coding-system-name coding-system)))))

(defun nxml-prepare-to-save ()
  (unless (and (not enable-multibyte-characters)
	       (local-variable-p 'buffer-file-coding-system)
	       buffer-file-coding-system
	       (or (eq (coding-system-type buffer-file-coding-system) 5)
		   (eq buffer-file-coding-system 'no-conversion)))
    (save-excursion
      (setq buffer-file-coding-system (nxml-select-coding-system))))
  ;; nil from a function in `write-contents-hooks' means
  ;; to continue and write the file as normal
  nil)

(defun nxml-select-coding-system ()
  (let* ((suitable-coding-systems
	  (find-coding-systems-region (point-min) (point-max)))
	 (enc-pos (progn
		    (goto-char (point-min))
		    (xmltok-get-declared-encoding-position)))
	 (enc-name
	  (and (consp enc-pos)
	       (buffer-substring-no-properties (car enc-pos)
					       (cdr enc-pos))))
	 (coding-system
	  (cond (enc-name
		 (if (string= (downcase enc-name) "utf-16")
		     (nxml-choose-utf-16-coding-system)
		   (nxml-mime-charset-coding-system enc-name)))
		(enc-pos (nxml-choose-utf-coding-system)))))
    ;; Make sure we have a coding-system
    (unless coding-system
      (setq coding-system
	    (and (not buffer-read-only)
		 (nxml-choose-suitable-coding-system
		  suitable-coding-systems)))
      (let ((message
	     (if enc-name
		 (format "Unknown encoding %s" enc-name)
	       "XML declaration is not well-formed")))
	(cond ((not coding-system)
	       (error "%s" message))
	      ((y-or-n-p
		(concat message
			". "
			(format (if enc-name
				    "Save with %s"
				  "Modify and save with encoding %s")
				(nxml-coding-system-name coding-system))
			" "))
	       (nxml-fix-encoding-declaration enc-pos coding-system))
	      (t (signal 'quit nil)))))
    ;; Make sure it can encode all the characters in the buffer
    (unless (or (memq (coding-system-base coding-system)
		      suitable-coding-systems)
		(equal suitable-coding-systems '(undecided)))
      (let ((message
	     (nxml-unsuitable-coding-system-message coding-system
						    enc-name)))
	(setq coding-system
	      (and (not buffer-read-only)
		   (nxml-choose-suitable-coding-system
		    suitable-coding-systems)))
	(cond ((not coding-system) (error "%s" message))
	      ((y-or-n-p (concat message
				 (format ". Save with %s "
					 (nxml-coding-system-name
					  coding-system))))
	       (nxml-fix-encoding-declaration enc-pos coding-system))
	      (t (signal 'quit nil)))))
    ;; Merge the newline type of our existing encoding
    (let ((current-eol-type
	   (coding-system-eol-type buffer-file-coding-system)))
      (when (and current-eol-type (integerp current-eol-type))
	(setq coding-system
	      (coding-system-change-eol-conversion coding-system
						   current-eol-type))))
    coding-system))

(defun nxml-unsuitable-coding-system-message (coding-system &optional enc-name)
  (if (nxml-coding-system-unicode-p coding-system)
      "Cannot translate some characters to Unicode"
    (format "Cannot encode some characters with %s"
	    (or enc-name
		(nxml-coding-system-name coding-system)))))

(defconst nxml-utf-16-coding-systems (and (coding-system-p 'utf-16-be)
					  (coding-system-p 'utf-16-le)
					  '(utf-16-be utf-16-le)))

(defconst nxml-utf-coding-systems (cons 'utf-8 nxml-utf-16-coding-systems))

(defun nxml-coding-system-unicode-p (coding-system)
  (nxml-coding-system-member (coding-system-base coding-system)
			     nxml-utf-coding-systems))

(defun nxml-coding-system-name (coding-system)
  (setq coding-system (coding-system-base coding-system))
  (symbol-name
   (if (nxml-coding-system-member coding-system nxml-utf-16-coding-systems)
       'utf-16
     (or (coding-system-get coding-system 'mime-charset)
	 coding-system))))

(defun nxml-fix-encoding-declaration (enc-pos coding-system)
  (let ((charset (nxml-coding-system-name coding-system)))
    (cond ((consp enc-pos)
	   (delete-region (car enc-pos) (cdr enc-pos))
	   (goto-char (car enc-pos))
	   (insert charset))
	  ((integerp enc-pos)
	   (goto-char enc-pos)
	   (insert " encoding=\"" charset ?\"))
	  (t
	   (goto-char (point-min))
	   (insert "<?xml version=\"1.0\" encoding=\""
		   charset
		   "\"?>\n")
	   (when (and (not enc-pos)
		      (let ((case-fold-search t))
			(looking-at xmltok-bad-xml-decl-regexp)))
	     (delete-region (point) (match-end 0)))))))

(defun nxml-choose-suitable-coding-system (suitable-coding-systems)
  (let (ret coding-system)
    (if (and buffer-file-coding-system
	     (memq (coding-system-base buffer-file-coding-system)
		   suitable-coding-systems))
	buffer-file-coding-system
      (while (and suitable-coding-systems (not ret))
	(setq coding-system (car suitable-coding-systems))
	(if (coding-system-get coding-system 'mime-charset)
	    (setq ret coding-system)
	  (setq suitable-coding-systems (cdr suitable-coding-systems))))
      ret)))

795
(defun nxml-choose-utf-coding-system ()
Mark A. Hershberger's avatar
Mark A. Hershberger committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
  (let ((cur (and (local-variable-p 'buffer-file-coding-system)
		  buffer-file-coding-system
		  (coding-system-base buffer-file-coding-system))))
    (cond ((car (nxml-coding-system-member cur nxml-utf-coding-systems)))
	  ((and nxml-prefer-utf-16-to-utf-8-flag
		(coding-system-p 'utf-16-le)
		(coding-system-p 'utf-16-be))
	   (if nxml-prefer-utf-16-little-to-big-endian-flag
	       'utf-16-le
	     'utf-16-be))
	  (t 'utf-8))))

(defun nxml-choose-utf-16-coding-system ()
  (let ((cur (and (local-variable-p 'buffer-file-coding-system)
		  buffer-file-coding-system
		  (coding-system-base buffer-file-coding-system))))
    (cond ((car (nxml-coding-system-member cur nxml-utf-16-coding-systems)))
	  (nxml-prefer-utf-16-little-to-big-endian-flag
	   (and (coding-system-p 'utf-16-le) 'utf-16-le))
	  (t (and (coding-system-p 'utf-16-be) 'utf-16-be)))))

(defun nxml-coding-system-member (coding-system coding-systems)
  (let (ret)
    (while (and coding-systems (not ret))
      (if (coding-system-equal coding-system
			       (car coding-systems))
	  (setq ret coding-systems)
	(setq coding-systems (cdr coding-systems))))
    ret))

;;; Fontification

Michael Olson's avatar
Michael Olson committed
828 829 830 831 832 833
(defun nxml-unfontify-region (start end)
  (font-lock-default-unfontify-region start end)
  (nxml-clear-char-ref-extra-display start end))

(defun nxml-extend-region ()
  "Extend the region to hold the minimum area we can fontify with nXML.
834
Called with `font-lock-beg' and `font-lock-end' dynamically bound."
Michael Olson's avatar
Michael Olson committed
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
  (let ((start font-lock-beg)
        (end font-lock-end))

    (nxml-debug-change "nxml-extend-region(input)" start end)

    (when (< start nxml-prolog-end)
      (setq start (point-min)))

    (cond ((<= end nxml-prolog-end)
           (setq end nxml-prolog-end))

          (t
           (goto-char start)
           ;; some font-lock backends (like Emacs 22 jit-lock) snap
           ;; the region to the beginning of the line no matter what
           ;; we say here. To mitigate the resulting excess
           ;; fontification, ignore leading whitespace.
           (skip-syntax-forward " ")

           ;; find the beginning of the previous tag
           (when (not (equal (char-after) ?\<))
             (search-backward "<" nxml-prolog-end t))
           (nxml-ensure-scan-up-to-date)
           (nxml-move-outside-backwards)
           (setq start (point))

           (while (< (point) end)
             (nxml-tokenize-forward))

           (setq end (point))))

    (when (or (< start font-lock-beg)
              (> end font-lock-end))
      (setq font-lock-beg start
            font-lock-end end)
      (nxml-debug-change "nxml-extend-region" start end)
      t)))

(defun nxml-fontify-matcher (bound)
  "Called as font-lock keyword matcher."

  (unless nxml-degraded
    (nxml-debug-change "nxml-fontify-matcher" (point) bound)

    (when (< (point) nxml-prolog-end)
880
      ;; Prolog needs to be fontified in one go, and
Michael Olson's avatar
Michael Olson committed
881
      ;; nxml-extend-region makes sure we start at BOB.
882
      (cl-assert (bobp))
Michael Olson's avatar
Michael Olson committed
883 884 885
      (nxml-fontify-prolog)
      (goto-char nxml-prolog-end))

886
    (let (xmltok-errors)
Michael Olson's avatar
Michael Olson committed
887
      (while (and (nxml-tokenize-forward)
888
                  (<= (point) bound))   ; Intervals are open-ended.
Michael Olson's avatar
Michael Olson committed
889 890
        (nxml-apply-fontify-rule)))

891
    )
Michael Olson's avatar
Michael Olson committed
892 893 894 895

  ;; Since we did the fontification internally, tell font-lock to not
  ;; do anything itself.
  nil)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915

(defun nxml-fontify-prolog ()
  "Fontify the prolog.
The buffer is assumed to be prepared for fontification.
This does not set the fontified property, but it does clear
faces appropriately."
  (let ((regions nxml-prolog-regions))
    (while regions
      (let ((region (car regions)))
	(nxml-apply-fontify-rule (aref region 0)
				 (aref region 1)
				 (aref region 2)))
      (setq regions (cdr regions)))))

;; Vectors identify a substring of the token to be highlighted in some face.

;; Token types returned by xmltok-forward.

(put 'start-tag
     'nxml-fontify-rule
916 917
     '([nil 1 nxml-tag-delimiter]
       [-1 nil nxml-tag-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
918 919 920 921 922
       (element-qname . 1)
       attributes))

(put 'partial-start-tag
     'nxml-fontify-rule
923
     '([nil 1 nxml-tag-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
924 925 926 927 928
       (element-qname . 1)
       attributes))

(put 'end-tag
     'nxml-fontify-rule
929 930 931
     '([nil 1 nxml-tag-delimiter]
       [1 2 nxml-tag-slash]
       [-1 nil nxml-tag-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
932 933 934 935
       (element-qname . 2)))

(put 'partial-end-tag
     'nxml-fontify-rule
936 937
     '([nil 1 nxml-tag-delimiter]
       [1 2 nxml-tag-slash]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
938 939 940 941
       (element-qname . 2)))

(put 'empty-element
     'nxml-fontify-rule
942 943 944
     '([nil 1 nxml-tag-delimiter]
       [-2 -1 nxml-tag-slash]
       [-1 nil nxml-tag-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
945 946 947 948 949
       (element-qname . 1)
       attributes))

(put 'partial-empty-element
     'nxml-fontify-rule
950 951
     '([nil 1 nxml-tag-delimiter]
       [-1 nil nxml-tag-slash]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
952 953 954 955 956
       (element-qname . 1)
       attributes))

(put 'char-ref
     'nxml-fontify-rule
957 958 959
     '([nil 2 nxml-char-ref-delimiter]
       [2 -1 nxml-char-ref-number]
       [-1 nil nxml-char-ref-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
960 961 962 963
       char-ref))

(put 'entity-ref
     'nxml-fontify-rule
964 965 966
     '([nil 1 nxml-entity-ref-delimiter]
       [1 -1 nxml-entity-ref-name]
       [-1 nil nxml-entity-ref-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
967 968 969

(put 'comment
     'nxml-fontify-rule
970 971 972
     '([nil 4 nxml-comment-delimiter]
       [4 -3 nxml-comment-content]
       [-3 nil nxml-comment-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
973 974 975

(put 'processing-instruction
     'nxml-fontify-rule
976 977
     '([nil 2 nxml-processing-instruction-delimiter]
       [-2 nil nxml-processing-instruction-delimiter]
Mark A. Hershberger's avatar
Mark A. Hershberger committed
978 979 980 981
       processing-instruction-content))

(put 'cdata-section
     'nxml-fontify-rule
982 983 984 985 986
     '([nil 3 nxml-cdata-section-delimiter] ; <![
       [3 8 nxml-cdata-section-CDATA] ; CDATA
       [8 9 nxml-cdata-section-delimiter] ; [
       [9 -3 nxml-cdata-section-content] ; ]]>
       [-3 nil nxml-cdata-section-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
987 988 989

(put 'data
     'nxml-fontify-rule
990
     '([nil nil nxml-text]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
991 992 993 994 995

;; Prolog region types in list returned by xmltok-forward-prolog.

(put 'xml-declaration
     'nxml-fontify-rule
996 997 998
     '([nil 2 nxml-processing-instruction-delimiter]
       [2 5 nxml-processing-instruction-target]
       [-2 nil nxml-processing-instruction-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
999 1000 1001

(put 'xml-declaration-attribute-name
     'nxml-fontify-rule
1002
     '([nil nil nxml-attribute-local-name]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1003 1004 1005

(put 'xml-declaration-attribute-value
     'nxml-fontify-rule
1006 1007 1008
     '([nil 1 nxml-attribute-value-delimiter]
       [1 -1 nxml-attribute-value]
       [-1 nil nxml-attribute-value-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1009 1010 1011

(put 'processing-instruction-left
     'nxml-fontify-rule
1012 1013
     '([nil 2 nxml-processing-instruction-delimiter]
       [2 nil nxml-processing-instruction-target]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1014 1015 1016

(put 'processing-instruction-right
     'nxml-fontify-rule
1017 1018
     '([nil -2 nxml-processing-instruction-content]
       [-2 nil nxml-processing-instruction-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1019 1020 1021

(put 'literal
     'nxml-fontify-rule
1022 1023 1024
     '([nil 1 nxml-prolog-literal-delimiter]
       [1 -1 nxml-prolog-literal-content]
       [-1 nil nxml-prolog-literal-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1025 1026 1027

(put 'keyword
     'nxml-fontify-rule
1028
     '([nil nil nxml-prolog-keyword]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1029 1030 1031

(put 'markup-declaration-open
     'nxml-fontify-rule
1032 1033
     '([0 2 nxml-markup-declaration-delimiter]
       [2 nil nxml-prolog-keyword]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1034 1035 1036

(put 'markup-declaration-close
     'nxml-fontify-rule
1037
     '([nil nil nxml-markup-declaration-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1038 1039 1040

(put 'internal-subset-open
     'nxml-fontify-rule
1041
     '([nil nil nxml-markup-declaration-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1042 1043 1044

(put 'internal-subset-close
     'nxml-fontify-rule
1045 1046
     '([nil 1 nxml-markup-declaration-delimiter]
       [-1 nil nxml-markup-declaration-delimiter]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1047 1048 1049

(put 'hash-name
     'nxml-fontify-rule
1050 1051
     '([nil 1 nxml-hash]
       [1 nil nxml-prolog-keyword]))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075

(defun nxml-apply-fontify-rule (&optional type start end)
  (let ((rule (get (or type xmltok-type) 'nxml-fontify-rule)))
    (unless start (setq start xmltok-start))
    (unless end (setq end (point)))
    (while rule
      (let* ((action (car rule)))
	(setq rule (cdr rule))
	(cond ((vectorp action)
	       (nxml-set-face (let ((offset (aref action 0)))
				(cond ((not offset) start)
				      ((< offset 0) (+ end offset))
				      (t (+ start offset))))
			      (let ((offset (aref action 1)))
				(cond ((not offset) end)
				      ((< offset 0) (+ end offset))
				      (t (+ start offset))))
			      (aref action 2)))
	      ((and (consp action)
		    (eq (car action) 'element-qname))
	       (when xmltok-name-end ; maybe nil in partial-end-tag case
		 (nxml-fontify-qname (+ start (cdr action))
				     xmltok-name-colon
				     xmltok-name-end
1076 1077 1078
				     'nxml-element-prefix
				     'nxml-element-colon
				     'nxml-element-local-name)))
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1079 1080 1081 1082 1083
	      ((eq action 'attributes)
	       (nxml-fontify-attributes))
	      ((eq action 'processing-instruction-content)
	       (nxml-set-face (+ start 2)
			      xmltok-name-end
1084
			      'nxml-processing-instruction-target)
Mark A. Hershberger's avatar
Mark A. Hershberger committed
1085 1086 1087 1088 1089
	       (nxml-set-face (save-excursion
				(goto-char xmltok-name-end)
				(skip-chars-forward " \t\r\n")
				(point))
			      (