| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
(eval-when-compile |
|---|
| 38 |
(require 'skeleton) |
|---|
| 39 |
(require 'outline) |
|---|
| 40 |
(require 'cl)) |
|---|
| 41 |
|
|---|
| 42 |
(defgroup sgml nil |
|---|
| 43 |
"SGML editing mode." |
|---|
| 44 |
:link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) |
|---|
| 45 |
:group 'languages) |
|---|
| 46 |
|
|---|
| 47 |
(defcustom sgml-basic-offset 2 |
|---|
| 48 |
"*Specifies the basic indentation level for `sgml-indent-line'." |
|---|
| 49 |
:type 'integer |
|---|
| 50 |
:group 'sgml) |
|---|
| 51 |
|
|---|
| 52 |
(defcustom sgml-transformation-function 'identity |
|---|
| 53 |
"*Default value for `skeleton-transformation-function' in SGML mode." |
|---|
| 54 |
:type 'function |
|---|
| 55 |
:group 'sgml) |
|---|
| 56 |
|
|---|
| 57 |
(put 'sgml-transformation-function 'variable-interactive |
|---|
| 58 |
"aTransformation function: ") |
|---|
| 59 |
(defvaralias 'sgml-transformation 'sgml-transformation-function) |
|---|
| 60 |
|
|---|
| 61 |
(defcustom sgml-mode-hook nil |
|---|
| 62 |
"Hook run by command `sgml-mode'. |
|---|
| 63 |
`text-mode-hook' is run first." |
|---|
| 64 |
:group 'sgml |
|---|
| 65 |
:type 'hook) |
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
(defvar sgml-specials '(?\") |
|---|
| 71 |
List of characters that have a special meaning for SGML mode. |
|---|
| 72 |
This list is used when first loading the `sgml-mode' library. |
|---|
| 73 |
The supported characters and potential disadvantages are: |
|---|
| 74 |
|
|---|
| 75 |
?\\\" Makes \" in text start a string. |
|---|
| 76 |
?' Makes ' in text start a string. |
|---|
| 77 |
?- Makes -- in text start a comment. |
|---|
| 78 |
|
|---|
| 79 |
When only one of ?\\\" or ?' are included, \"'\" or '\"', as can be found in |
|---|
| 80 |
DTDs, start a string. To partially avoid this problem this also makes these |
|---|
| 81 |
self insert as named entities depending on `sgml-quick-keys'. |
|---|
| 82 |
|
|---|
| 83 |
Including ?- has the problem of affecting dashes that have nothing to do |
|---|
| 84 |
with comments, so we normally turn it off.") |
|---|
| 85 |
|
|---|
| 86 |
(defvar sgml-quick-keys nil |
|---|
| 87 |
"Use <, >, &, /, SPC and `sgml-specials' keys \"electrically\" when non-nil. |
|---|
| 88 |
This takes effect when first loading the `sgml-mode' library.") |
|---|
| 89 |
|
|---|
| 90 |
(defvar sgml-mode-map |
|---|
| 91 |
(let ((map (make-keymap)) |
|---|
| 92 |
(menu-map (make-sparse-keymap "SGML"))) |
|---|
| 93 |
(define-key map "\C-c\C-i" 'sgml-tags-invisible) |
|---|
| 94 |
(define-key map "/" 'sgml-slash) |
|---|
| 95 |
(define-key map "\C-c\C-n" 'sgml-name-char) |
|---|
| 96 |
(define-key map "\C-c\C-t" 'sgml-tag) |
|---|
| 97 |
(define-key map "\C-c\C-a" 'sgml-attributes) |
|---|
| 98 |
(define-key map "\C-c\C-b" 'sgml-skip-tag-backward) |
|---|
| 99 |
(define-key map [?\C-c left] 'sgml-skip-tag-backward) |
|---|
| 100 |
(define-key map "\C-c\C-f" 'sgml-skip-tag-forward) |
|---|
| 101 |
(define-key map [?\C-c right] 'sgml-skip-tag-forward) |
|---|
| 102 |
(define-key map "\C-c\C-d" 'sgml-delete-tag) |
|---|
| 103 |
(define-key map "\C-c\^?" 'sgml-delete-tag) |
|---|
| 104 |
(define-key map "\C-c?" 'sgml-tag-help) |
|---|
| 105 |
(define-key map "\C-c/" 'sgml-close-tag) |
|---|
| 106 |
(define-key map "\C-c8" 'sgml-name-8bit-mode) |
|---|
| 107 |
(define-key map "\C-c\C-v" 'sgml-validate) |
|---|
| 108 |
(when sgml-quick-keys |
|---|
| 109 |
(define-key map "&" 'sgml-name-char) |
|---|
| 110 |
(define-key map "<" 'sgml-tag) |
|---|
| 111 |
(define-key map " " 'sgml-auto-attributes) |
|---|
| 112 |
(define-key map ">" 'sgml-maybe-end-tag) |
|---|
| 113 |
(when (memq ?\" sgml-specials) |
|---|
| 114 |
(define-key map "\"" 'sgml-name-self)) |
|---|
| 115 |
(when (memq ?' sgml-specials) |
|---|
| 116 |
(define-key map "'" 'sgml-name-self))) |
|---|
| 117 |
(define-key map (vector (make-char 'latin-iso8859-1)) |
|---|
| 118 |
'sgml-maybe-name-self) |
|---|
| 119 |
(let ((c 127) |
|---|
| 120 |
(map (nth 1 map))) |
|---|
| 121 |
(while (< (setq c (1+ c)) 256) |
|---|
| 122 |
(aset map c 'sgml-maybe-name-self))) |
|---|
| 123 |
(define-key map [menu-bar sgml] (cons "SGML" menu-map)) |
|---|
| 124 |
(define-key menu-map [sgml-validate] '("Validate" . sgml-validate)) |
|---|
| 125 |
(define-key menu-map [sgml-name-8bit-mode] |
|---|
| 126 |
'("Toggle 8 Bit Insertion" . sgml-name-8bit-mode)) |
|---|
| 127 |
(define-key menu-map [sgml-tags-invisible] |
|---|
| 128 |
'("Toggle Tag Visibility" . sgml-tags-invisible)) |
|---|
| 129 |
(define-key menu-map [sgml-tag-help] |
|---|
| 130 |
'("Describe Tag" . sgml-tag-help)) |
|---|
| 131 |
(define-key menu-map [sgml-delete-tag] |
|---|
| 132 |
'("Delete Tag" . sgml-delete-tag)) |
|---|
| 133 |
(define-key menu-map [sgml-skip-tag-forward] |
|---|
| 134 |
'("Forward Tag" . sgml-skip-tag-forward)) |
|---|
| 135 |
(define-key menu-map [sgml-skip-tag-backward] |
|---|
| 136 |
'("Backward Tag" . sgml-skip-tag-backward)) |
|---|
| 137 |
(define-key menu-map [sgml-attributes] |
|---|
| 138 |
'("Insert Attributes" . sgml-attributes)) |
|---|
| 139 |
(define-key menu-map [sgml-tag] '("Insert Tag" . sgml-tag)) |
|---|
| 140 |
map) |
|---|
| 141 |
"Keymap for SGML mode. See also `sgml-specials'.") |
|---|
| 142 |
|
|---|
| 143 |
(defun sgml-make-syntax-table (specials) |
|---|
| 144 |
(let ((table (make-syntax-table text-mode-syntax-table))) |
|---|
| 145 |
(modify-syntax-entry ?< "(>" table) |
|---|
| 146 |
(modify-syntax-entry ?> ")<" table) |
|---|
| 147 |
(modify-syntax-entry ?: "_" table) |
|---|
| 148 |
(modify-syntax-entry ?_ "_" table) |
|---|
| 149 |
(modify-syntax-entry ?. "_" table) |
|---|
| 150 |
(if (memq ?- specials) |
|---|
| 151 |
(modify-syntax-entry ?- "_ 1234" table)) |
|---|
| 152 |
(if (memq ?\" specials) |
|---|
| 153 |
(modify-syntax-entry ?\" "\"\"" table)) |
|---|
| 154 |
(if (memq ?' specials) |
|---|
| 155 |
(modify-syntax-entry ?\' "\"'" table)) |
|---|
| 156 |
table)) |
|---|
| 157 |
|
|---|
| 158 |
(defvar sgml-mode-syntax-table (sgml-make-syntax-table sgml-specials) |
|---|
| 159 |
"Syntax table used in SGML mode. See also `sgml-specials'.") |
|---|
| 160 |
|
|---|
| 161 |
(defconst sgml-tag-syntax-table |
|---|
| 162 |
(let ((table (sgml-make-syntax-table '(?- ?\" ?\')))) |
|---|
| 163 |
(dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/)) |
|---|
| 164 |
(modify-syntax-entry char "." table)) |
|---|
| 165 |
table) |
|---|
| 166 |
"Syntax table used to parse SGML tags.") |
|---|
| 167 |
|
|---|
| 168 |
(defcustom sgml-name-8bit-mode nil |
|---|
| 169 |
"*When non-nil, insert non-ASCII characters as named entities." |
|---|
| 170 |
:type 'boolean |
|---|
| 171 |
:group 'sgml) |
|---|
| 172 |
|
|---|
| 173 |
(defvar sgml-char-names |
|---|
| 174 |
[nil nil nil nil nil nil nil nil |
|---|
| 175 |
nil nil nil nil nil nil nil nil |
|---|
| 176 |
nil nil nil nil nil nil nil nil |
|---|
| 177 |
nil nil nil nil nil nil nil nil |
|---|
| 178 |
"nbsp" "excl" "quot" "num" "dollar" "percnt" "amp" "apos" |
|---|
| 179 |
"lpar" "rpar" "ast" "plus" "comma" "hyphen" "period" "sol" |
|---|
| 180 |
nil nil nil nil nil nil nil nil |
|---|
| 181 |
nil nil "colon" "semi" "lt" "eq" "gt" "quest" |
|---|
| 182 |
"commat" nil nil nil nil nil nil nil |
|---|
| 183 |
nil nil nil nil nil nil nil nil |
|---|
| 184 |
nil nil nil nil nil nil nil nil |
|---|
| 185 |
nil nil nil "lsqb" nil "rsqb" "uarr" "lowbar" |
|---|
| 186 |
"lsquo" nil nil nil nil nil nil nil |
|---|
| 187 |
nil nil nil nil nil nil nil nil |
|---|
| 188 |
nil nil nil nil nil nil nil nil |
|---|
| 189 |
nil nil nil "lcub" "verbar" "rcub" "tilde" nil |
|---|
| 190 |
nil nil nil nil nil nil nil nil |
|---|
| 191 |
nil nil nil nil nil nil nil nil |
|---|
| 192 |
nil nil nil nil nil nil nil nil |
|---|
| 193 |
nil nil nil nil nil nil nil nil |
|---|
| 194 |
"nbsp" "iexcl" "cent" "pound" "curren" "yen" "brvbar" "sect" |
|---|
| 195 |
"uml" "copy" "ordf" "laquo" "not" "shy" "reg" "macr" |
|---|
| 196 |
"ring" "plusmn" "sup2" "sup3" "acute" "micro" "para" "middot" |
|---|
| 197 |
"cedil" "sup1" "ordm" "raquo" "frac14" "frac12" "frac34" "iquest" |
|---|
| 198 |
"Agrave" "Aacute" "Acirc" "Atilde" "Auml" "Aring" "AElig" "Ccedil" |
|---|
| 199 |
"Egrave" "Eacute" "Ecirc" "Euml" "Igrave" "Iacute" "Icirc" "Iuml" |
|---|
| 200 |
"ETH" "Ntilde" "Ograve" "Oacute" "Ocirc" "Otilde" "Ouml" nil |
|---|
| 201 |
"Oslash" "Ugrave" "Uacute" "Ucirc" "Uuml" "Yacute" "THORN" "szlig" |
|---|
| 202 |
"agrave" "aacute" "acirc" "atilde" "auml" "aring" "aelig" "ccedil" |
|---|
| 203 |
"egrave" "eacute" "ecirc" "euml" "igrave" "iacute" "icirc" "iuml" |
|---|
| 204 |
"eth" "ntilde" "ograve" "oacute" "ocirc" "otilde" "ouml" "divide" |
|---|
| 205 |
"oslash" "ugrave" "uacute" "ucirc" "uuml" "yacute" "thorn" "yuml"] |
|---|
| 206 |
"Vector of symbolic character names without `&' and ` |
|---|
| 207 |
|
|---|
| 208 |
(put 'sgml-table 'char-table-extra-slots 0) |
|---|
| 209 |
|
|---|
| 210 |
(defvar sgml-char-names-table |
|---|
| 211 |
(let ((table (make-char-table 'sgml-table)) |
|---|
| 212 |
(i 32) |
|---|
| 213 |
elt) |
|---|
| 214 |
(while (< i 256) |
|---|
| 215 |
(setq elt (aref sgml-char-names i)) |
|---|
| 216 |
(if elt (aset table (make-char 'latin-iso8859-1 i) elt)) |
|---|
| 217 |
(setq i (1+ i))) |
|---|
| 218 |
table) |
|---|
| 219 |
"A table for mapping non-ASCII characters into SGML entity names. |
|---|
| 220 |
Currently, only Latin-1 characters are supported.") |
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 |
|
|---|
| 226 |
|
|---|
| 227 |
(defcustom sgml-validate-command "nsgmls -s" |
|---|
| 228 |
"*The command to validate an SGML document. |
|---|
| 229 |
The file name of current buffer file name will be appended to this, |
|---|
| 230 |
separated by a space." |
|---|
| 231 |
:type 'string |
|---|
| 232 |
:version "21.1" |
|---|
| 233 |
:group 'sgml) |
|---|
| 234 |
|
|---|
| 235 |
(defvar sgml-saved-validate-command nil |
|---|
| 236 |
"The command last used to validate in this buffer.") |
|---|
| 237 |
|
|---|
| 238 |
|
|---|
| 239 |
|
|---|
| 240 |
(defcustom sgml-slash-distance 1000 |
|---|
| 241 |
"*If non-nil, is the maximum distance to search for matching `/'." |
|---|
| 242 |
:type '(choice (const nil) integer) |
|---|
| 243 |
:group 'sgml) |
|---|
| 244 |
|
|---|
| 245 |
(defconst sgml-namespace-re "[_[:alpha:]][-_.[:alnum:]]*") |
|---|
| 246 |
(defconst sgml-name-re "[_:[:alpha:]][-_.:[:alnum:]]*") |
|---|
| 247 |
(defconst sgml-tag-name-re (concat "<\\([!/?]?" sgml-name-re "\\)")) |
|---|
| 248 |
(defconst sgml-attrs-re "\\(?:[^\"'/><]\\|\"[^\"]*\"\\|'[^']*'\\)*") |
|---|
| 249 |
(defconst sgml-start-tag-regex (concat "<" sgml-name-re sgml-attrs-re) |
|---|
| 250 |
"Regular expression that matches a non-empty start tag. |
|---|
| 251 |
Any terminating `>' or `/' is not matched.") |
|---|
| 252 |
|
|---|
| 253 |
(defface sgml-namespace |
|---|
| 254 |
'((t (:inherit font-lock-builtin-face))) |
|---|
| 255 |
"`sgml-mode' face used to highlight the namespace part of identifiers." |
|---|
| 256 |
:group 'sgml) |
|---|
| 257 |
(defvar sgml-namespace-face 'sgml-namespace) |
|---|
| 258 |
|
|---|
| 259 |
|
|---|
| 260 |
(defconst sgml-font-lock-keywords-1 |
|---|
| 261 |
`((,(concat "<\\([!?]" sgml-name-re "\\)") 1 font-lock-keyword-face) |
|---|
| 262 |
|
|---|
| 263 |
|
|---|
| 264 |
(,(concat "</?\\(" sgml-namespace-re "\\)\\(?::\\(" sgml-name-re "\\)\\)?") |
|---|
| 265 |
(1 (if (match-end 2) sgml-namespace-face font-lock-function-name-face)) |
|---|
| 266 |
(2 font-lock-function-name-face nil t)) |
|---|
| 267 |
|
|---|
| 268 |
|
|---|
| 269 |
|
|---|
| 270 |
|
|---|
| 271 |
(,(concat "\\(?:^\\|[ \t]\\)\\(" sgml-namespace-re "\\)\\(?::\\(" |
|---|
| 272 |
sgml-name-re "\\)\\)?=[\"']") |
|---|
| 273 |
(1 (if (match-end 2) sgml-namespace-face font-lock-variable-name-face)) |
|---|
| 274 |
(2 font-lock-variable-name-face nil t)) |
|---|
| 275 |
(,(concat "[&%]" sgml-name-re ";?") . font-lock-variable-name-face))) |
|---|
| 276 |
|
|---|
| 277 |
(defconst sgml-font-lock-keywords-2 |
|---|
| 278 |
(append |
|---|
| 279 |
sgml-font-lock-keywords-1 |
|---|
| 280 |
'((eval |
|---|
| 281 |
. (cons (concat "<" |
|---|
| 282 |
(regexp-opt (mapcar 'car sgml-tag-face-alist) t) |
|---|
| 283 |
"\\([ \t][^>]*\\)?>\\([^<]+\\)</\\1>") |
|---|
| 284 |
'(3 (cdr (assoc (downcase (match-string 1)) |
|---|
| 285 |
sgml-tag-face-alist)) prepend)))))) |
|---|
| 286 |
|
|---|
| 287 |
|
|---|
| 288 |
|
|---|
| 289 |
(defvar sgml-font-lock-keywords sgml-font-lock-keywords-1 |
|---|
| 290 |
"*Rules for highlighting SGML code. See also `sgml-tag-face-alist'.") |
|---|
| 291 |
|
|---|
| 292 |
(defvar sgml-font-lock-syntactic-keywords |
|---|
| 293 |
|
|---|
| 294 |
|
|---|
| 295 |
|
|---|
| 296 |
'(("\\(<\\)!--" (1 "< b")) |
|---|
| 297 |
("--[ \t\n]*\\(>\\)" (1 "> b"))) |
|---|
| 298 |
"Syntactic keywords for `sgml-mode'.") |
|---|
| 299 |
|
|---|
| 300 |
|
|---|
| 301 |
(defvar sgml-face-tag-alist () |
|---|
| 302 |
"Alist of face and tag name for facemenu.") |
|---|
| 303 |
|
|---|
| 304 |
(defvar sgml-tag-face-alist () |
|---|
| 305 |
"Tag names and face or list of faces to fontify with when invisible. |
|---|
| 306 |
When `font-lock-maximum-decoration' is 1 this is always used for fontifying. |
|---|
| 307 |
When more these are fontified together with `sgml-font-lock-keywords'.") |
|---|
| 308 |
|
|---|
| 309 |
(defvar sgml-display-text () |
|---|
| 310 |
"Tag names as lowercase symbols, and display string when invisible.") |
|---|
| 311 |
|
|---|
| 312 |
|
|---|
| 313 |
(defvar sgml-tags-invisible nil) |
|---|
| 314 |
|
|---|
| 315 |
(defcustom sgml-tag-alist |
|---|
| 316 |
'(("![" ("ignore" t) ("include" t)) |
|---|
| 317 |
("!attlist") |
|---|
| 318 |
("!doctype") |
|---|
| 319 |
("!element") |
|---|
| 320 |
("!entity")) |
|---|
| 321 |
"*Alist of tag names for completing read and insertion rules. |
|---|
| 322 |
This alist is made up as |
|---|
| 323 |
|
|---|
| 324 |
((\"tag\" . TAGRULE) |
|---|
| 325 |
...) |
|---|
| 326 |
|
|---|
| 327 |
TAGRULE is a list of optionally t (no endtag) or `\\n' (separate endtag by |
|---|
| 328 |
newlines) or a skeleton with nil, t or `\\n' in place of the interactor |
|---|
| 329 |
followed by an ATTRIBUTERULE (for an always present attribute) or an |
|---|
| 330 |
attribute alist. |
|---|
| 331 |
|
|---|
| 332 |
The attribute alist is made up as |
|---|
| 333 |
|
|---|
| 334 |
((\"attribute\" . ATTRIBUTERULE) |
|---|
| 335 |
...) |
|---|
| 336 |
|
|---|
| 337 |
ATTRIBUTERULE is a list of optionally t (no value when no input) followed by |
|---|
| 338 |
an optional alist of possible values." |
|---|
| 339 |
:type '(repeat (cons (string :tag "Tag Name") |
|---|
| 340 |
(repeat :tag "Tag Rule" sexp))) |
|---|
| 341 |
:group 'sgml) |
|---|
| 342 |
(put 'sgml-tag-alist 'risky-local-variable t) |
|---|
| 343 |
|
|---|
| 344 |
(defcustom sgml-tag-help |
|---|
| 345 |
'(("!" . "Empty declaration for comment") |
|---|
| 346 |
("![" . "Embed declarations with parser directive") |
|---|
| 347 |
("!attlist" . "Tag attributes declaration") |
|---|
| 348 |
("!doctype" . "Document type (DTD) declaration") |
|---|
| 349 |
("!element" . "Tag declaration") |
|---|
| 350 |
("!entity" . "Entity (macro) declaration")) |
|---|
| 351 |
"*Alist of tag name and short description." |
|---|
| 352 |
:type '(repeat (cons (string :tag "Tag Name") |
|---|
| 353 |
(string :tag "Description"))) |
|---|
| 354 |
:group 'sgml) |
|---|
| 355 |
|
|---|
| 356 |
(defcustom sgml-xml-mode nil |
|---|
| 357 |
"*When non-nil, tag insertion functions will be XML-compliant. |
|---|
| 358 |
If this variable is customized, the custom value is used always. |
|---|
| 359 |
Otherwise, it is set to be buffer-local when the file has |
|---|
| 360 |
a DOCTYPE or an XML declaration." |
|---|
| 361 |
:type 'boolean |
|---|
| 362 |
:version "22.1" |
|---|
| 363 |
:group 'sgml) |
|---|
| 364 |
|
|---|
| 365 |
(defvar sgml-empty-tags nil |
|---|
| 366 |
"List of tags whose !ELEMENT definition says EMPTY.") |
|---|
| 367 |
|
|---|
| 368 |
(defvar sgml-unclosed-tags nil |
|---|
| 369 |
"List of tags whose !ELEMENT definition says the end-tag is optional.") |
|---|
| 370 |
|
|---|
| 371 |
(defun sgml-xml-guess () |
|---|
| 372 |
"Guess whether the current buffer is XML." |
|---|
| 373 |
(save-excursion |
|---|
| 374 |
(goto-char (point-min)) |
|---|
| 375 |
(when (or (string= "xml" (file-name-extension (or buffer-file-name ""))) |
|---|
| 376 |
(looking-at "\\s-*<\\?xml") |
|---|
| 377 |
(when (re-search-forward |
|---|
| 378 |
(eval-when-compile |
|---|
| 379 |
(mapconcat 'identity |
|---|
| 380 |
'("<!DOCTYPE" "\\(\\w+\\)" "\\(\\w+\\)" |
|---|
| 381 |
"\"\\([^\"]+\\)\"" "\"\\([^\"]+\\)\"") |
|---|
| 382 |
"\\s-+")) |
|---|
| 383 |
nil t) |
|---|
| 384 |
(string-match "X\\(HT\\)?ML" (match-string 3)))) |
|---|
| 385 |
(set (make-local-variable 'sgml-xml-mode) t)))) |
|---|
| 386 |
|
|---|
| 387 |
(defvar v2) |
|---|
| 388 |
|
|---|
| 389 |
(defun sgml-comment-indent-new-line (&optional soft) |
|---|
| 390 |
(let ((comment-start "-- ") |
|---|
| 391 |
(comment-start-skip "\\(<!\\)?--[ \t]*") |
|---|
| 392 |
(comment-end " --") |
|---|
| 393 |
(comment-style 'plain)) |
|---|
| 394 |
(comment-indent-new-line soft))) |
|---|
| 395 |
|
|---|
| 396 |
(defun sgml-mode-facemenu-add-face-function (face end) |
|---|
| 397 |
(if (setq face (cdr (assq face sgml-face-tag-alist))) |
|---|
| 398 |
(progn |
|---|
| 399 |
(setq face (funcall skeleton-transformation-function face)) |
|---|
| 400 |
(setq facemenu-end-add-face (concat "</" face ">")) |
|---|
| 401 |
(concat "<" face ">")) |
|---|
| 402 |
(error "Face not configured for %s mode" mode-name))) |
|---|
| 403 |
|
|---|
| 404 |
(defun sgml-fill-nobreak () |
|---|
| 405 |
|
|---|
| 406 |
(save-excursion |
|---|
| 407 |
(skip-chars-backward " \t") |
|---|
| 408 |
(and (not (zerop (skip-syntax-backward "w_"))) |
|---|
| 409 |
(skip-chars-backward "/?!") |
|---|
| 410 |
(eq (char-before) ?<)))) |
|---|
| 411 |
|
|---|
| 412 |
|
|---|
| 413 |
(define-derived-mode sgml-mode text-mode "SGML" |
|---|
| 414 |
"Major mode for editing SGML documents. |
|---|
| 415 |
Makes > match <. |
|---|
| 416 |
Keys <, &, SPC within <>, \", / and ' can be electric depending on |
|---|
| 417 |
`sgml-quick-keys'. |
|---|
| 418 |
|
|---|
| 419 |
An argument of N to a tag-inserting command means to wrap it around |
|---|
| 420 |
the next N words. In Transient Mark mode, when the mark is active, |
|---|
| 421 |
N defaults to -1, which means to wrap it around the current region. |
|---|
| 422 |
|
|---|
| 423 |
If you like upcased tags, put (setq sgml-transformation-function 'upcase) |
|---|
| 424 |
in your `.emacs' file. |
|---|
| 425 |
|
|---|
| 426 |
Use \\[sgml-validate] to validate your document with an SGML parser. |
|---|
| 427 |
|
|---|
| 428 |
Do \\[describe-variable] sgml- SPC to see available variables. |
|---|
| 429 |
Do \\[describe-key] on the following bindings to discover what they do. |
|---|
| 430 |
\\{sgml-mode-map}" |
|---|
| 431 |
(make-local-variable 'sgml-saved-validate-command) |
|---|
| 432 |
(make-local-variable 'facemenu-end-add-face) |
|---|
| 433 |
|
|---|
| 434 |
|
|---|
| 435 |
|
|---|
| 436 |
|
|---|
| 437 |
(set (make-local-variable 'paragraph-start) (concat "[ \t]*$\\|\ |
|---|
| 438 |
sgml-name-re sgml-attrs-re "\\)?>")) |
|---|
| 439 |
(set (make-local-variable 'paragraph-separate) |
|---|
| 440 |
(concat paragraph-start "$")) |
|---|
| 441 |
(set (make-local-variable 'adaptive-fill-regexp) "[ \t]*") |
|---|
| 442 |
(add-hook 'fill-nobreak-predicate 'sgml-fill-nobreak nil t) |
|---|
| 443 |
(set (make-local-variable 'indent-line-function) 'sgml-indent-line) |
|---|
| 444 |
(set (make-local-variable 'comment-start) "<!-- ") |
|---|
| 445 |
(set (make-local-variable 'comment-end) " -->") |
|---|
| 446 |
(set (make-local-variable 'comment-indent-function) 'sgml-comment-indent) |
|---|
| 447 |
(set (make-local-variable 'comment-line-break-function) |
|---|
| 448 |
'sgml-comment-indent-new-line) |
|---|
| 449 |
(set (make-local-variable 'skeleton-further-elements) |
|---|
| 450 |
'((completion-ignore-case t))) |
|---|
| 451 |
(set (make-local-variable 'skeleton-end-hook) |
|---|
| 452 |
(lambda () |
|---|
| 453 |
(or (eolp) |
|---|
| 454 |
(not (or (eq v2 '\n) (eq (car-safe v2) '\n))) |
|---|
| 455 |
(newline-and-indent)))) |
|---|
| 456 |
(set (make-local-variable 'font-lock-defaults) |
|---|
| 457 |
'((sgml-font-lock-keywords |
|---|
| 458 |
sgml-font-lock-keywords-1 |
|---|
| 459 |
sgml-font-lock-keywords-2) |
|---|
| 460 |
nil t nil nil |
|---|
| 461 |
(font-lock-syntactic-keywords |
|---|
| 462 |
. sgml-font-lock-syntactic-keywords))) |
|---|
| 463 |
(set (make-local-variable 'facemenu-add-face-function) |
|---|
| 464 |
'sgml-mode-facemenu-add-face-function) |
|---|
| 465 |
(sgml-xml-guess) |
|---|
| 466 |
(if sgml-xml-mode |
|---|
| 467 |
(setq mode-name "XML") |
|---|
| 468 |
(set (make-local-variable 'skeleton-transformation-function) |
|---|
| 469 |
sgml-transformation-function)) |
|---|
| 470 |
|
|---|
| 471 |
|
|---|
| 472 |
|
|---|
| 473 |
|
|---|
| 474 |
|
|---|
| 475 |
|
|---|
| 476 |
(set (make-local-variable 'comment-start-skip) "<!--[ \t]*") |
|---|
| 477 |
(set (make-local-variable 'comment-end-skip) "[ \t]*--[ \t\n]*>") |
|---|
| 478 |
|
|---|
| 479 |
(setq imenu-generic-expression |
|---|
| 480 |
`((nil |
|---|
| 481 |
,(concat "<!\\(element\\|entity\\)[ \t\n]+%?[ \t\n]*\\(" |
|---|
| 482 |
sgml-name-re "\\)") |
|---|
| 483 |
2) |
|---|
| 484 |
("Id" |
|---|
| 485 |
,(concat "<[^>]+[ \t\n]+[Ii][Dd]=\\(['\"]" |
|---|
| 486 |
(if sgml-xml-mode "" "?") |
|---|
| 487 |
"\\)\\(" sgml-name-re "\\)\\1") |
|---|
| 488 |
2) |
|---|
| 489 |
("Name" |
|---|
| 490 |
,(concat "<[^>]+[ \t\n]+[Nn][Aa][Mm][Ee]=\\(['\"]" |
|---|
| 491 |
(if sgml-xml-mode "" "?") |
|---|
| 492 |
"\\)\\(" sgml-name-re "\\)\\1") |
|---|
| 493 |
2)))) |
|---|
| 494 |
|
|---|
| 495 |
|
|---|
| 496 |
|
|---|
| 497 |
|
|---|
| 498 |
(defalias 'xml-mode 'sgml-mode) |
|---|
| 499 |
|
|---|
| 500 |
(defun sgml-comment-indent () |
|---|
| 501 |
(if (looking-at "--") comment-column 0)) |
|---|
| 502 |
|
|---|
| 503 |
(defun sgml-slash (arg) |
|---|
| 504 |
"Insert ARG slash characters. |
|---|
| 505 |
Behaves electrically if `sgml-quick-keys' is non-nil." |
|---|
| 506 |
(interactive "p") |
|---|
| 507 |
(cond |
|---|
| 508 |
((not (and (eq (char-before) ?<) (= arg 1))) |
|---|
| 509 |
(sgml-slash-matching arg)) |
|---|
| 510 |
((eq sgml-quick-keys 'indent) |
|---|
| 511 |
(insert-char ?/ 1) |
|---|
| 512 |
(indent-according-to-mode)) |
|---|
| 513 |
((eq sgml-quick-keys 'close) |
|---|
| 514 |
(delete-backward-char 1) |
|---|
| 515 |
(sgml-close-tag)) |
|---|
| 516 |
(t |
|---|
| 517 |
(sgml-slash-matching arg)))) |
|---|
| 518 |
|
|---|
| 519 |
(defun sgml-slash-matching (arg) |
|---|
| 520 |
"Insert `/' and display any previous matching `/'. |
|---|
| 521 |
Two `/'s are treated as matching if the first `/' ends a net-enabling |
|---|
| 522 |
start tag, and the second `/' is the corresponding null end tag." |
|---|
| 523 |
(interactive "p") |
|---|
| 524 |
(insert-char ?/ arg) |
|---|
| 525 |
(if (> arg 0) |
|---|
| 526 |
(let ((oldpos (point)) |
|---|
| 527 |
(blinkpos) |
|---|
| 528 |
(level 0)) |
|---|
| 529 |
(save-excursion |
|---|
| 530 |
(save-restriction |
|---|
| 531 |
(if sgml-slash-distance |
|---|
| 532 |
(narrow-to-region (max (point-min) |
|---|
| 533 |
(- (point) sgml-slash-distance)) |
|---|
| 534 |
oldpos)) |
|---|
| 535 |
(if (and (re-search-backward sgml-start-tag-regex (point-min) t) |
|---|
| 536 |
(eq (match-end 0) (1- oldpos))) |
|---|
| 537 |
() |
|---|
| 538 |
(goto-char (1- oldpos)) |
|---|
| 539 |
(while (and (not blinkpos) |
|---|
| 540 |
(search-backward "/" (point-min) t)) |
|---|
| 541 |
(let ((tagend (save-excursion |
|---|
| 542 |
(if (re-search-backward sgml-start-tag-regex |
|---|
| 543 |
(point-min) t) |
|---|
| 544 |
(match-end 0) |
|---|
| 545 |
nil)))) |
|---|
| 546 |
(if (eq tagend (point)) |
|---|
| 547 |
(if (eq level 0) |
|---|
| 548 |
(setq blinkpos (point)) |
|---|
| 549 |
(setq level (1- level))) |
|---|
| 550 |
(setq level (1+ level))))))) |
|---|
| 551 |
(when blinkpos |
|---|
| 552 |
(goto-char blinkpos) |
|---|
| 553 |
(if (pos-visible-in-window-p) |
|---|
| 554 |
(sit-for 1) |
|---|
| 555 |
(message "Matches %s" |
|---|
| 556 |
(buffer-substring (line-beginning-position) |
|---|
| 557 |
(1+ blinkpos))))))))) |
|---|
| 558 |
|
|---|
| 559 |
|
|---|
| 560 |
|
|---|
| 561 |
|
|---|
| 562 |
(defun sgml-name-char (&optional char) |
|---|
| 563 |
"Insert a symbolic character name according to `sgml-char-names'. |
|---|
| 564 |
Non-ASCII chars may be inserted either with the meta key, as in M-SPC for |
|---|
| 565 |
no-break space or M-- for a soft hyphen; or via an input method or |
|---|
| 566 |
encoded keyboard operation." |
|---|
| 567 |
(interactive "*") |
|---|
| 568 |
(insert ?&) |
|---|
| 569 |
(or char |
|---|
| 570 |
(setq char (read-quoted-char "Enter char or octal number"))) |
|---|
| 571 |
(delete-backward-char 1) |
|---|
| 572 |
(insert char) |
|---|
| 573 |
(undo-boundary) |
|---|
| 574 |
(sgml-namify-char)) |
|---|
| 575 |
|
|---|
| 576 |
(defun sgml-namify-char () |
|---|
| 577 |
"Change the char before point into its `&name;' equivalent. |
|---|
| 578 |
Uses `sgml-char-names'." |
|---|
| 579 |
(interactive) |
|---|
| 580 |
(let* ((char (char-before)) |
|---|
| 581 |
(name |
|---|
| 582 |
(cond |
|---|
| 583 |
((null char) (error "No char before point")) |
|---|
| 584 |
((< char 256) (or (aref sgml-char-names char) char)) |
|---|
| 585 |
((aref sgml-char-names-table char)) |
|---|
| 586 |
((encode-char char 'ucs))))) |
|---|
| 587 |
(if (not name) |
|---|
| 588 |
(error "Don't know the name of `%c'" char) |
|---|
| 589 |
(delete-backward-char 1) |
|---|
| 590 |
(insert (format (if (numberp name) "&#%d;" "&%s;") name))))) |
|---|
| 591 |
|
|---|
| 592 |
(defun sgml-name-self () |
|---|
| 593 |
"Insert a symbolic character name according to `sgml-char-names'." |
|---|
| 594 |
(interactive "*") |
|---|
| 595 |
(sgml-name-char last-command-char)) |
|---|
| 596 |
|
|---|
| 597 |
(defun sgml-maybe-name-self () |
|---|
| 598 |
"Insert a symbolic character name according to `sgml-char-names'." |
|---|
| 599 |
(interactive "*") |
|---|
| 600 |
(if sgml-name-8bit-mode |
|---|
| 601 |
(let ((mc last-command-char)) |
|---|
| 602 |
(if (< mc 256) |
|---|
| 603 |
(setq mc (unibyte-char-to-multibyte mc))) |
|---|
| 604 |
(or mc (setq mc last-command-char)) |
|---|
| 605 |
(sgml-name-char mc)) |
|---|
| 606 |
(self-insert-command 1))) |
|---|
| 607 |
|
|---|
| 608 |
(defun sgml-name-8bit-mode () |
|---|
| 609 |
"Toggle whether to insert named entities instead of non-ASCII characters. |
|---|
| 610 |
This only works for Latin-1 input." |
|---|
| 611 |
(interactive) |
|---|
| 612 |
(setq sgml-name-8bit-mode (not sgml-name-8bit-mode)) |
|---|
| 613 |
(message "sgml name entity mode is now %s" |
|---|
| 614 |
(if sgml-name-8bit-mode "ON" "OFF"))) |
|---|
| 615 |
|
|---|
| 616 |
|
|---|
| 617 |
|
|---|
| 618 |
|
|---|
| 619 |
|
|---|
| 620 |
|
|---|
| 621 |
(defvar sgml-tag-last nil) |
|---|
| 622 |
(defvar sgml-tag-history nil) |
|---|
| 623 |
(define-skeleton sgml-tag |
|---|
| 624 |
"Prompt for a tag and insert it, optionally with attributes. |
|---|
| 625 |
Completion and configuration are done according to `sgml-tag-alist'. |
|---|
| 626 |
If you like tags and attributes in uppercase do \\[set-variable] |
|---|
| 627 |
`skeleton-transformation-function' RET `upcase' RET, or put this |
|---|
| 628 |
in your `.emacs': |
|---|
| 629 |
(setq sgml-transformation-function 'upcase)" |
|---|
| 630 |
(funcall (or skeleton-transformation-function 'identity) |
|---|
| 631 |
(setq sgml-tag-last |
|---|
| 632 |
(completing-read |
|---|
| 633 |
(if (> (length sgml-tag-last) 0) |
|---|
| 634 |
(format "Tag (default %s): " sgml-tag-last) |
|---|
| 635 |
"Tag: ") |
|---|
| 636 |
sgml-tag-alist nil nil nil 'sgml-tag-history sgml-tag-last))) |
|---|
| 637 |
?< str | |
|---|
| 638 |
(("") -1 '(undo-boundary) (identity "<")) | |
|---|
| 639 |
`(("") '(setq v2 (sgml-attributes ,str t)) ?> |
|---|
| 640 |
(cond |
|---|
| 641 |
((string= "![" ,str) |
|---|
| 642 |
(backward-char) |
|---|
| 643 |
'(("") " [ " _ " ]]")) |
|---|
| 644 |
((and (eq v2 t) sgml-xml-mode (member ,str sgml-empty-tags)) |
|---|
| 645 |
'(("") -1 " />")) |
|---|
| 646 |
((or (and (eq v2 t) (not sgml-xml-mode)) (string-match "^[/!?]" ,str)) |
|---|
| 647 |
nil) |
|---|
| 648 |
((symbolp v2) |
|---|
| 649 |
|
|---|
| 650 |
|
|---|
| 651 |
(if (eq v2 t) (setq v2 nil)) |
|---|
| 652 |
|
|---|
| 653 |
|
|---|
| 654 |
'(("") v2 _ v2 "</" (identity ',str) ?>)) |
|---|
| 655 |
((eq (car v2) t) |
|---|
| 656 |
(cons '("") (cdr v2))) |
|---|
| 657 |
(t |
|---|
| 658 |
(append '(("") (car v2)) |
|---|
| 659 |
(cdr v2) |
|---|
| 660 |
'(resume: (car v2) _ "</" (identity ',str) ?>)))))) |
|---|
| 661 |
|
|---|
| 662 |
(autoload 'skeleton-read "skeleton") |
|---|
| 663 |
|
|---|
| 664 |
(defun sgml-attributes (tag &optional quiet) |
|---|
| 665 |
"When at top level of a tag, interactively insert attributes. |
|---|
| 666 |
|
|---|
| 667 |
Completion and configuration of TAG are done according to `sgml-tag-alist'. |
|---|
| 668 |
If QUIET, do not print a message when there are no attributes for TAG." |
|---|
| 669 |
(interactive (list (save-excursion (sgml-beginning-of-tag t)))) |
|---|
| 670 |
(or (stringp tag) (error "Wrong context for adding attribute")) |
|---|
| 671 |
(if tag |
|---|
| 672 |
(let ((completion-ignore-case t) |
|---|
| 673 |
(alist (cdr (assoc (downcase tag) sgml-tag-alist))) |
|---|
| 674 |
car attribute i) |
|---|
| 675 |
(if (or (symbolp (car alist)) |
|---|
| 676 |
(symbolp (car (car alist)))) |
|---|
| 677 |
(setq car (car alist) |
|---|
| 678 |
alist (cdr alist))) |
|---|
| 679 |
(or quiet |
|---|
| 680 |
(message "No attributes configured.")) |
|---|
| 681 |
(if (stringp (car alist)) |
|---|
| 682 |
(progn |
|---|
| 683 |
(insert (if (eq (preceding-char) ?\s) "" ?\s) |
|---|
| 684 |
(funcall skeleton-transformation-function (car alist))) |
|---|
| 685 |
(sgml-value alist)) |
|---|
| 686 |
(setq i (length alist)) |
|---|
| 687 |
(while (> i 0) |
|---|
| 688 |
(insert ?\s) |
|---|
| 689 |
(insert (funcall skeleton-transformation-function |
|---|
| 690 |
(setq attribute |
|---|
| 691 |
(skeleton-read '(completing-read |
|---|
| 692 |
"Attribute: " |
|---|
| 693 |
alist))))) |
|---|
| 694 |
(if (string= "" attribute) |
|---|
| 695 |
(setq i 0) |
|---|
| 696 |
(sgml-value (assoc (downcase attribute) alist)) |
|---|
| 697 |
(setq i (1- i)))) |
|---|
| 698 |
(if (eq (preceding-char) ?\s) |
|---|
| 699 |
(delete-backward-char 1))) |
|---|
| 700 |
car))) |
|---|
| 701 |
|
|---|
| 702 |
(defun sgml-auto-attributes (arg) |
|---|
| 703 |
"Self insert the character typed; at top level of tag, prompt for attributes. |
|---|
| 704 |
With prefix argument, only self insert." |
|---|
| 705 |
(interactive "*P") |
|---|
| 706 |
(let ((point (point)) |
|---|
| 707 |
tag) |
|---|
| 708 |
(if (or arg |
|---|
| 709 |
(not sgml-tag-alist) |
|---|
| 710 |
(symbolp (setq tag (save-excursion (sgml-beginning-of-tag t)))) |
|---|
| 711 |
(eq (aref tag 0) ?/)) |
|---|
| 712 |
(self-insert-command (prefix-numeric-value arg)) |
|---|
| 713 |
(sgml-attributes tag) |
|---|
| 714 |
(setq last-command-char ?\s) |
|---|
| 715 |
(or (> (point) point) |
|---|
| 716 |
(self-insert-command 1))))) |
|---|
| 717 |
|
|---|
| 718 |
(defun sgml-tag-help (&optional tag) |
|---|
| 719 |
"Display description of tag TAG. If TAG is omitted, use the tag at point." |
|---|
| 720 |
(interactive) |
|---|
| 721 |
(or tag |
|---|
| 722 |
(save-excursion |
|---|
| 723 |
(if (eq (following-char) ?<) |
|---|
| 724 |
(forward-char)) |
|---|
| 725 |
(setq tag (sgml-beginning-of-tag)))) |
|---|
| 726 |
(or (stringp tag) |
|---|
| 727 |
(error "No tag selected")) |
|---|
| 728 |
(setq tag (downcase tag)) |
|---|
| 729 |
(message "%s" |
|---|
| 730 |
(or (cdr (assoc (downcase tag) sgml-tag-help)) |
|---|
| 731 |
(and (eq (aref tag 0) ?/) |
|---|
| 732 |
(cdr (assoc (downcase (substring tag 1)) sgml-tag-help))) |
|---|
| 733 |
"No description available"))) |
|---|
| 734 |
|
|---|
| 735 |
(defun sgml-maybe-end-tag (&optional arg) |
|---|
| 736 |
"Name self unless in position to end a tag or a prefix ARG is given." |
|---|
| 737 |
(interactive "P") |
|---|
| 738 |
(if (or arg (eq (car (sgml-lexical-context)) 'tag)) |
|---|
| 739 |
(self-insert-command (prefix-numeric-value arg)) |
|---|
| 740 |
(sgml-name-self))) |
|---|
| 741 |
|
|---|
| 742 |
(defun sgml-skip-tag-backward (arg) |
|---|
| 743 |
"Skip to beginning of tag or matching opening tag if present. |
|---|
| 744 |
With prefix argument ARG, repeat this ARG times." |
|---|
| 745 |
(interactive "p") |
|---|
| 746 |
|
|---|
| 747 |
(while (>= arg 1) |
|---|
| 748 |
(search-backward "<" nil t) |
|---|
| 749 |
(if (looking-at "</\\([^ \n\t>]+\\)") |
|---|
| 750 |
|
|---|
| 751 |
(let ((case-fold-search t) |
|---|
| 752 |
(re (concat "</?" (regexp-quote (match-string 1)) |
|---|
| 753 |
|
|---|
| 754 |
"\\([^>]*[^/>]\\)?>"))) |
|---|
| 755 |
(while (and (re-search-backward re nil t) |
|---|
| 756 |
(eq (char-after (1+ (point))) ?/)) |
|---|
| 757 |
(forward-char 1) |
|---|
| 758 |
(sgml-skip-tag-backward 1)))) |
|---|
| 759 |
(setq arg (1- arg)))) |
|---|
| 760 |
|
|---|
| 761 |
(defun sgml-skip-tag-forward (arg) |
|---|
| 762 |
"Skip to end of tag or matching closing tag if present. |
|---|
| 763 |
With prefix argument ARG, repeat this ARG times. |
|---|
| 764 |
Return t if after a closing tag." |
|---|
| 765 |
(interactive "p") |
|---|
| 766 |
|
|---|
| 767 |
|
|---|
| 768 |
|
|---|
| 769 |
(let ((return t)) |
|---|
| 770 |
(with-syntax-table sgml-tag-syntax-table |
|---|
| 771 |
(while (>= arg 1) |
|---|
| 772 |
(skip-chars-forward "^<>") |
|---|
| 773 |
(if (eq (following-char) ?> |
|---|