sgml-mode.el 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293
  1. ;;; sgml-mode.el --- SGML- and HTML-editing modes -*- lexical-binding:t -*-
  2. ;; Copyright (C) 1992, 1995-1996, 1998, 2001-2015 Free Software
  3. ;; Foundation, Inc.
  4. ;; Author: James Clark <jjc@jclark.com>
  5. ;; Maintainer: emacs-devel@gnu.org
  6. ;; Adapted-By: ESR, Daniel Pfeiffer <occitan@esperanto.org>,
  7. ;; F.Potorti@cnuce.cnr.it
  8. ;; Keywords: wp, hypermedia, comm, languages
  9. ;; This file is part of GNU Emacs.
  10. ;; GNU Emacs is free software: you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation, either version 3 of the License, or
  13. ;; (at your option) any later version.
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;; Configurable major mode for editing document in the SGML standard general
  22. ;; markup language. As an example contains a mode for editing the derived
  23. ;; HTML hypertext markup language.
  24. ;;; Code:
  25. (eval-when-compile
  26. (require 'skeleton)
  27. (require 'cl-lib))
  28. (defgroup sgml nil
  29. "SGML editing mode."
  30. :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
  31. :group 'languages)
  32. (defcustom sgml-basic-offset 2
  33. "Specifies the basic indentation level for `sgml-indent-line'."
  34. :type 'integer
  35. :group 'sgml)
  36. (defcustom sgml-attribute-offset 0
  37. "Specifies a delta for attribute indentation in `sgml-indent-line'.
  38. When 0, attribute indentation looks like this:
  39. <element
  40. attribute=\"value\">
  41. </element>
  42. When 2, attribute indentation looks like this:
  43. <element
  44. attribute=\"value\">
  45. </element>"
  46. :version "25.1"
  47. :type 'integer
  48. :safe 'integerp
  49. :group 'sgml)
  50. (defcustom sgml-xml-mode nil
  51. "When non-nil, tag insertion functions will be XML-compliant.
  52. It is set to be buffer-local when the file has
  53. a DOCTYPE or an XML declaration."
  54. :type 'boolean
  55. :version "22.1"
  56. :group 'sgml)
  57. (defcustom sgml-transformation-function 'identity
  58. "Default value for `skeleton-transformation-function' in SGML mode."
  59. :type 'function
  60. :initialize 'custom-initialize-default
  61. :set (lambda (sym val)
  62. (set-default sym val)
  63. (mapc (lambda (buff)
  64. (with-current-buffer buff
  65. (and (derived-mode-p 'sgml-mode)
  66. (not sgml-xml-mode)
  67. (setq skeleton-transformation-function val))))
  68. (buffer-list)))
  69. :group 'sgml)
  70. (put 'sgml-transformation-function 'variable-interactive
  71. "aTransformation function: ")
  72. (defvaralias 'sgml-transformation 'sgml-transformation-function)
  73. (defcustom sgml-mode-hook nil
  74. "Hook run by command `sgml-mode'.
  75. `text-mode-hook' is run first."
  76. :group 'sgml
  77. :type 'hook)
  78. ;; As long as Emacs's syntax can't be complemented with predicates to context
  79. ;; sensitively confirm the syntax of characters, we have to live with this
  80. ;; kludgy kind of tradeoff.
  81. (defvar sgml-specials '(?\")
  82. "List of characters that have a special meaning for SGML mode.
  83. This list is used when first loading the `sgml-mode' library.
  84. The supported characters and potential disadvantages are:
  85. ?\\\" Makes \" in text start a string.
  86. ?\\=' Makes \\=' in text start a string.
  87. ?- Makes -- in text start a comment.
  88. When only one of ?\\\" or ?\\=' are included, \"\\='\" or \\='\"\\=', as can be found in
  89. DTDs, start a string. To partially avoid this problem this also makes these
  90. self insert as named entities depending on `sgml-quick-keys'.
  91. Including ?- has the problem of affecting dashes that have nothing to do
  92. with comments, so we normally turn it off.")
  93. (defvar sgml-quick-keys nil
  94. "Use <, >, &, /, SPC and `sgml-specials' keys \"electrically\" when non-nil.
  95. This takes effect when first loading the `sgml-mode' library.")
  96. (defvar sgml-mode-map
  97. (let ((map (make-keymap)) ;`sparse' doesn't allow binding to charsets.
  98. (menu-map (make-sparse-keymap "SGML")))
  99. (define-key map "\C-c\C-i" 'sgml-tags-invisible)
  100. (define-key map "/" 'sgml-slash)
  101. (define-key map "\C-c\C-n" 'sgml-name-char)
  102. (define-key map "\C-c\C-t" 'sgml-tag)
  103. (define-key map "\C-c\C-a" 'sgml-attributes)
  104. (define-key map "\C-c\C-b" 'sgml-skip-tag-backward)
  105. (define-key map [?\C-c left] 'sgml-skip-tag-backward)
  106. (define-key map "\C-c\C-f" 'sgml-skip-tag-forward)
  107. (define-key map [?\C-c right] 'sgml-skip-tag-forward)
  108. (define-key map "\C-c\C-d" 'sgml-delete-tag)
  109. (define-key map "\C-c\^?" 'sgml-delete-tag)
  110. (define-key map "\C-c?" 'sgml-tag-help)
  111. (define-key map "\C-c]" 'sgml-close-tag)
  112. (define-key map "\C-c/" 'sgml-close-tag)
  113. ;; Redundant keybindings, for consistency with TeX mode.
  114. (define-key map "\C-c\C-o" 'sgml-tag)
  115. (define-key map "\C-c\C-e" 'sgml-close-tag)
  116. (define-key map "\C-c8" 'sgml-name-8bit-mode)
  117. (define-key map "\C-c\C-v" 'sgml-validate)
  118. (when sgml-quick-keys
  119. (define-key map "&" 'sgml-name-char)
  120. (define-key map "<" 'sgml-tag)
  121. (define-key map " " 'sgml-auto-attributes)
  122. (define-key map ">" 'sgml-maybe-end-tag)
  123. (when (memq ?\" sgml-specials)
  124. (define-key map "\"" 'sgml-name-self))
  125. (when (memq ?' sgml-specials)
  126. (define-key map "'" 'sgml-name-self)))
  127. (let ((c 127)
  128. (map (nth 1 map)))
  129. (while (< (setq c (1+ c)) 256)
  130. (aset map c 'sgml-maybe-name-self)))
  131. (define-key map [menu-bar sgml] (cons "SGML" menu-map))
  132. (define-key menu-map [sgml-validate] '("Validate" . sgml-validate))
  133. (define-key menu-map [sgml-name-8bit-mode]
  134. '("Toggle 8 Bit Insertion" . sgml-name-8bit-mode))
  135. (define-key menu-map [sgml-tags-invisible]
  136. '("Toggle Tag Visibility" . sgml-tags-invisible))
  137. (define-key menu-map [sgml-tag-help]
  138. '("Describe Tag" . sgml-tag-help))
  139. (define-key menu-map [sgml-delete-tag]
  140. '("Delete Tag" . sgml-delete-tag))
  141. (define-key menu-map [sgml-skip-tag-forward]
  142. '("Forward Tag" . sgml-skip-tag-forward))
  143. (define-key menu-map [sgml-skip-tag-backward]
  144. '("Backward Tag" . sgml-skip-tag-backward))
  145. (define-key menu-map [sgml-attributes]
  146. '("Insert Attributes" . sgml-attributes))
  147. (define-key menu-map [sgml-tag] '("Insert Tag" . sgml-tag))
  148. map)
  149. "Keymap for SGML mode. See also `sgml-specials'.")
  150. (defun sgml-make-syntax-table (specials)
  151. (let ((table (make-syntax-table text-mode-syntax-table)))
  152. (modify-syntax-entry ?< "(>" table)
  153. (modify-syntax-entry ?> ")<" table)
  154. (modify-syntax-entry ?: "_" table)
  155. (modify-syntax-entry ?_ "_" table)
  156. (modify-syntax-entry ?. "_" table)
  157. (if (memq ?- specials)
  158. (modify-syntax-entry ?- "_ 1234" table))
  159. (if (memq ?\" specials)
  160. (modify-syntax-entry ?\" "\"\"" table))
  161. (if (memq ?' specials)
  162. (modify-syntax-entry ?\' "\"'" table))
  163. table))
  164. (defvar sgml-mode-syntax-table (sgml-make-syntax-table sgml-specials)
  165. "Syntax table used in SGML mode. See also `sgml-specials'.")
  166. (defconst sgml-tag-syntax-table
  167. (let ((table (sgml-make-syntax-table sgml-specials)))
  168. (dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/))
  169. (modify-syntax-entry char "." table))
  170. (unless (memq ?' sgml-specials)
  171. ;; Avoid that skipping a tag backwards skips any "'" prefixing it.
  172. (modify-syntax-entry ?' "w" table))
  173. table)
  174. "Syntax table used to parse SGML tags.")
  175. (defcustom sgml-name-8bit-mode nil
  176. "When non-nil, insert non-ASCII characters as named entities."
  177. :type 'boolean
  178. :group 'sgml)
  179. (defvar sgml-char-names
  180. [nil nil nil nil nil nil nil nil
  181. nil nil nil nil nil nil nil nil
  182. nil nil nil nil nil nil nil nil
  183. nil nil nil nil nil nil nil nil
  184. "nbsp" "excl" "quot" "num" "dollar" "percnt" "amp" "apos"
  185. "lpar" "rpar" "ast" "plus" "comma" "hyphen" "period" "sol"
  186. nil nil nil nil nil nil nil nil
  187. nil nil "colon" "semi" "lt" "eq" "gt" "quest"
  188. "commat" nil nil nil nil nil nil nil
  189. nil nil nil nil nil nil nil nil
  190. nil nil nil nil nil nil nil nil
  191. nil nil nil "lsqb" nil "rsqb" "uarr" "lowbar"
  192. "lsquo" nil nil nil nil nil nil nil
  193. nil nil nil nil nil nil nil nil
  194. nil nil nil nil nil nil nil nil
  195. nil nil nil "lcub" "verbar" "rcub" "tilde" nil
  196. nil nil nil nil nil nil nil nil
  197. nil nil nil nil nil nil nil nil
  198. nil nil nil nil nil nil nil nil
  199. nil nil nil nil nil nil nil nil
  200. "nbsp" "iexcl" "cent" "pound" "curren" "yen" "brvbar" "sect"
  201. "uml" "copy" "ordf" "laquo" "not" "shy" "reg" "macr"
  202. "ring" "plusmn" "sup2" "sup3" "acute" "micro" "para" "middot"
  203. "cedil" "sup1" "ordm" "raquo" "frac14" "frac12" "frac34" "iquest"
  204. "Agrave" "Aacute" "Acirc" "Atilde" "Auml" "Aring" "AElig" "Ccedil"
  205. "Egrave" "Eacute" "Ecirc" "Euml" "Igrave" "Iacute" "Icirc" "Iuml"
  206. "ETH" "Ntilde" "Ograve" "Oacute" "Ocirc" "Otilde" "Ouml" nil
  207. "Oslash" "Ugrave" "Uacute" "Ucirc" "Uuml" "Yacute" "THORN" "szlig"
  208. "agrave" "aacute" "acirc" "atilde" "auml" "aring" "aelig" "ccedil"
  209. "egrave" "eacute" "ecirc" "euml" "igrave" "iacute" "icirc" "iuml"
  210. "eth" "ntilde" "ograve" "oacute" "ocirc" "otilde" "ouml" "divide"
  211. "oslash" "ugrave" "uacute" "ucirc" "uuml" "yacute" "thorn" "yuml"]
  212. "Vector of symbolic character names without `&' and `;'.")
  213. (put 'sgml-table 'char-table-extra-slots 0)
  214. (defvar sgml-char-names-table
  215. (let ((table (make-char-table 'sgml-table))
  216. (i 32)
  217. elt)
  218. (while (< i 128)
  219. (setq elt (aref sgml-char-names i))
  220. (if elt (aset table (make-char 'latin-iso8859-1 i) elt))
  221. (setq i (1+ i)))
  222. table)
  223. "A table for mapping non-ASCII characters into SGML entity names.
  224. Currently, only Latin-1 characters are supported.")
  225. (defcustom sgml-validate-command
  226. ;; prefer tidy because (o)nsgmls is often built without --enable-http
  227. ;; which makes it next to useless
  228. (cond ((executable-find "tidy")
  229. ;; tidy is available from http://tidy.sourceforge.net/
  230. "tidy --gnu-emacs yes -utf8 -e -q")
  231. ((executable-find "nsgmls")
  232. ;; nsgmls is a free SGML parser in the SP suite available from
  233. ;; ftp.jclark.com, replaced old `sgmls'.
  234. "nsgmls -s")
  235. ((executable-find "onsgmls")
  236. ;; onsgmls is the community version of `nsgmls'
  237. ;; hosted on http://openjade.sourceforge.net/
  238. "onsgmls -s")
  239. (t "Install (o)nsgmls, tidy, or some other SGML validator, and set `sgml-validate-command'"))
  240. "The command to validate an SGML document.
  241. The file name of current buffer file name will be appended to this,
  242. separated by a space."
  243. :type 'string
  244. :version "21.1"
  245. :group 'sgml)
  246. (defvar sgml-saved-validate-command nil
  247. "The command last used to validate in this buffer.")
  248. ;; I doubt that null end tags are used much for large elements,
  249. ;; so use a small distance here.
  250. (defcustom sgml-slash-distance 1000
  251. "If non-nil, is the maximum distance to search for matching `/'."
  252. :type '(choice (const nil) integer)
  253. :group 'sgml)
  254. (defconst sgml-namespace-re "[_[:alpha:]][-_.[:alnum:]]*")
  255. (defconst sgml-name-re "[_:[:alpha:]][-_.:[:alnum:]]*")
  256. (defconst sgml-tag-name-re (concat "<\\([!/?]?" sgml-name-re "\\)"))
  257. (defconst sgml-attrs-re "\\(?:[^\"'/><]\\|\"[^\"]*\"\\|'[^']*'\\)*")
  258. (defconst sgml-start-tag-regex (concat "<" sgml-name-re sgml-attrs-re)
  259. "Regular expression that matches a non-empty start tag.
  260. Any terminating `>' or `/' is not matched.")
  261. (defface sgml-namespace
  262. '((t (:inherit font-lock-builtin-face)))
  263. "`sgml-mode' face used to highlight the namespace part of identifiers."
  264. :group 'sgml)
  265. (defvar sgml-namespace-face 'sgml-namespace)
  266. ;; internal
  267. (defconst sgml-font-lock-keywords-1
  268. `((,(concat "<\\([!?]" sgml-name-re "\\)") 1 font-lock-keyword-face)
  269. ;; We could use the simpler "\\(" sgml-namespace-re ":\\)?" instead,
  270. ;; but it would cause a bit more backtracking in the re-matcher.
  271. (,(concat "</?\\(" sgml-namespace-re "\\)\\(?::\\(" sgml-name-re "\\)\\)?")
  272. (1 (if (match-end 2) sgml-namespace-face font-lock-function-name-face))
  273. (2 font-lock-function-name-face nil t))
  274. ;; FIXME: this doesn't cover the variables using a default value.
  275. ;; The first shy-group is an important anchor: it prevents an O(n^2)
  276. ;; pathological case where we otherwise keep retrying a failing match
  277. ;; against a very long word at every possible position within the word.
  278. (,(concat "\\(?:^\\|[ \t]\\)\\(" sgml-namespace-re "\\)\\(?::\\("
  279. sgml-name-re "\\)\\)?=[\"']")
  280. (1 (if (match-end 2) sgml-namespace-face font-lock-variable-name-face))
  281. (2 font-lock-variable-name-face nil t))
  282. (,(concat "[&%]" sgml-name-re ";?") . font-lock-variable-name-face)))
  283. (defconst sgml-font-lock-keywords-2
  284. (append
  285. sgml-font-lock-keywords-1
  286. '((eval
  287. . (cons (concat "<"
  288. (regexp-opt (mapcar 'car sgml-tag-face-alist) t)
  289. "\\([ \t][^>]*\\)?>\\([^<]+\\)</\\1>")
  290. '(3 (cdr (assoc-string (match-string 1) sgml-tag-face-alist t))
  291. prepend))))))
  292. ;; for font-lock, but must be defvar'ed after
  293. ;; sgml-font-lock-keywords-1 and sgml-font-lock-keywords-2 above
  294. (defvar sgml-font-lock-keywords sgml-font-lock-keywords-1
  295. "Rules for highlighting SGML code. See also `sgml-tag-face-alist'.")
  296. (defconst sgml-syntax-propertize-function
  297. (syntax-propertize-rules
  298. ;; Use the `b' style of comments to avoid interference with the -- ... --
  299. ;; comments recognized when `sgml-specials' includes ?-.
  300. ;; FIXME: beware of <!--> blabla <!--> !!
  301. ("\\(<\\)!--" (1 "< b"))
  302. ("--[ \t\n]*\\(>\\)" (1 "> b"))
  303. ;; Double quotes outside of tags should not introduce strings.
  304. ;; Be careful to call `syntax-ppss' on a position before the one we're
  305. ;; going to change, so as not to need to flush the data we just computed.
  306. ("\"" (0 (if (prog1 (zerop (car (syntax-ppss (match-beginning 0))))
  307. (goto-char (match-end 0)))
  308. (string-to-syntax ".")))))
  309. "Syntactic keywords for `sgml-mode'.")
  310. ;; internal
  311. (defvar sgml-face-tag-alist ()
  312. "Alist of face and tag name for facemenu.")
  313. (defvar sgml-tag-face-alist ()
  314. "Tag names and face or list of faces to fontify with when invisible.
  315. When `font-lock-maximum-decoration' is 1 this is always used for fontifying.
  316. When more these are fontified together with `sgml-font-lock-keywords'.")
  317. (defvar sgml-display-text ()
  318. "Tag names as lowercase symbols, and display string when invisible.")
  319. ;; internal
  320. (defvar sgml-tags-invisible nil)
  321. (defcustom sgml-tag-alist
  322. '(("![" ("ignore" t) ("include" t))
  323. ("!attlist")
  324. ("!doctype")
  325. ("!element")
  326. ("!entity"))
  327. "Alist of tag names for completing read and insertion rules.
  328. This alist is made up as
  329. ((\"tag\" . TAGRULE)
  330. ...)
  331. TAGRULE is a list of optionally t (no endtag) or `\\n' (separate endtag by
  332. newlines) or a skeleton with nil, t or `\\n' in place of the interactor
  333. followed by an ATTRIBUTERULE (for an always present attribute) or an
  334. attribute alist.
  335. The attribute alist is made up as
  336. ((\"attribute\" . ATTRIBUTERULE)
  337. ...)
  338. ATTRIBUTERULE is a list of optionally t (no value when no input) followed by
  339. an optional alist of possible values."
  340. :type '(repeat (cons (string :tag "Tag Name")
  341. (repeat :tag "Tag Rule" sexp)))
  342. :group 'sgml)
  343. (put 'sgml-tag-alist 'risky-local-variable t)
  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. (defvar sgml-empty-tags nil
  356. "List of tags whose !ELEMENT definition says EMPTY.")
  357. (defvar sgml-unclosed-tags nil
  358. "List of tags whose !ELEMENT definition says the end-tag is optional.")
  359. (defun sgml-xml-guess ()
  360. "Guess whether the current buffer is XML. Return non-nil if so."
  361. (save-excursion
  362. (goto-char (point-min))
  363. (or (string= "xml" (file-name-extension (or buffer-file-name "")))
  364. ;; Maybe the buffer-size check isn't needed, I don't know.
  365. (and (zerop (buffer-size))
  366. (string= "xhtml" (file-name-extension (or buffer-file-name ""))))
  367. (looking-at "\\s-*<\\?xml")
  368. (when (re-search-forward
  369. (eval-when-compile
  370. (mapconcat 'identity
  371. '("<!DOCTYPE" "\\(\\w+\\)" "\\(\\w+\\)"
  372. "\"\\([^\"]+\\)\"" "\"\\([^\"]+\\)\"")
  373. "\\s-+"))
  374. nil t)
  375. (string-match "X\\(HT\\)?ML" (match-string 3))))))
  376. (defvar v2) ; free for skeleton
  377. (defun sgml-comment-indent-new-line (&optional soft)
  378. (let ((comment-start "-- ")
  379. (comment-start-skip "\\(<!\\)?--[ \t]*")
  380. (comment-end " --")
  381. (comment-style 'plain))
  382. (comment-indent-new-line soft)))
  383. (defun sgml-mode-facemenu-add-face-function (face _end)
  384. (let ((tag-face (cdr (assq face sgml-face-tag-alist))))
  385. (cond (tag-face
  386. (setq tag-face (funcall skeleton-transformation-function tag-face))
  387. (setq facemenu-end-add-face (concat "</" tag-face ">"))
  388. (concat "<" tag-face ">"))
  389. ((and (consp face)
  390. (consp (car face))
  391. (null (cdr face))
  392. (memq (caar face) '(:foreground :background)))
  393. (setq facemenu-end-add-face "</span>")
  394. (format "<span style=\"%s:%s\">"
  395. (if (eq (caar face) :foreground)
  396. "color"
  397. "background-color")
  398. (cadr (car face))))
  399. (t
  400. (error "Face not configured for %s mode"
  401. (format-mode-line mode-name))))))
  402. (defun sgml-fill-nobreak ()
  403. "Don't break between a tag name and its first argument.
  404. This function is designed for use in `fill-nobreak-predicate'.
  405. <a href=\"some://where\" type=\"text/plain\">
  406. ^ ^
  407. | no break here | but still allowed here"
  408. (save-excursion
  409. (skip-chars-backward " \t")
  410. (and (not (zerop (skip-syntax-backward "w_")))
  411. (skip-chars-backward "/?!")
  412. (eq (char-before) ?<))))
  413. (defvar tildify-space-string)
  414. (defvar tildify-foreach-region-function)
  415. ;;;###autoload
  416. (define-derived-mode sgml-mode text-mode '(sgml-xml-mode "XML" "SGML")
  417. "Major mode for editing SGML documents.
  418. Makes > match <.
  419. Keys <, &, SPC within <>, \", / and \\=' can be electric depending on
  420. `sgml-quick-keys'.
  421. An argument of N to a tag-inserting command means to wrap it around
  422. the next N words. In Transient Mark mode, when the mark is active,
  423. N defaults to -1, which means to wrap it around the current region.
  424. If you like upcased tags, put (setq sgml-transformation-function \\='upcase)
  425. in your init file.
  426. Use \\[sgml-validate] to validate your document with an SGML parser.
  427. Do \\[describe-variable] sgml- SPC to see available variables.
  428. Do \\[describe-key] on the following bindings to discover what they do.
  429. \\{sgml-mode-map}"
  430. (make-local-variable 'sgml-saved-validate-command)
  431. (make-local-variable 'facemenu-end-add-face)
  432. ;; If encoding does not allow non-break space character, use reference.
  433. ;; FIXME: Perhaps use &nbsp; if possible (e.g. when we know its HTML)?
  434. (setq-local tildify-space-string
  435. (if (equal (decode-coding-string
  436. (encode-coding-string " " buffer-file-coding-system)
  437. buffer-file-coding-system) " ")
  438. " " "&#160;"))
  439. ;; FIXME: Use the fact that we're parsing the document already
  440. ;; rather than using regex-based filtering.
  441. (setq-local tildify-foreach-region-function
  442. (apply-partially
  443. 'tildify-foreach-ignore-environments
  444. `((,(eval-when-compile
  445. (concat
  446. "<\\("
  447. (regexp-opt '("pre" "dfn" "code" "samp" "kbd" "var"
  448. "PRE" "DFN" "CODE" "SAMP" "KBD" "VAR"))
  449. "\\)\\>[^>]*>"))
  450. . ("</" 1 ">"))
  451. ("<! *--" . "-- *>")
  452. ("<" . ">"))))
  453. ;;(make-local-variable 'facemenu-remove-face-function)
  454. ;; A start or end tag by itself on a line separates a paragraph.
  455. ;; This is desirable because SGML discards a newline that appears
  456. ;; immediately after a start tag or immediately before an end tag.
  457. (setq-local paragraph-start (concat "[ \t]*$\\|\
  458. [ \t]*</?\\(" sgml-name-re sgml-attrs-re "\\)?>"))
  459. (setq-local paragraph-separate (concat paragraph-start "$"))
  460. (setq-local adaptive-fill-regexp "[ \t]*")
  461. (add-hook 'fill-nobreak-predicate 'sgml-fill-nobreak nil t)
  462. (setq-local indent-line-function 'sgml-indent-line)
  463. (setq-local comment-start "<!-- ")
  464. (setq-local comment-end " -->")
  465. (setq-local comment-indent-function 'sgml-comment-indent)
  466. (setq-local comment-line-break-function 'sgml-comment-indent-new-line)
  467. (setq-local skeleton-further-elements '((completion-ignore-case t)))
  468. (setq-local skeleton-end-hook
  469. (lambda ()
  470. (or (eolp)
  471. (not (or (eq v2 '\n) (eq (car-safe v2) '\n)))
  472. (newline-and-indent))))
  473. (setq font-lock-defaults '((sgml-font-lock-keywords
  474. sgml-font-lock-keywords-1
  475. sgml-font-lock-keywords-2)
  476. nil t))
  477. (setq-local syntax-propertize-function sgml-syntax-propertize-function)
  478. (setq-local facemenu-add-face-function 'sgml-mode-facemenu-add-face-function)
  479. (setq-local sgml-xml-mode (sgml-xml-guess))
  480. (unless sgml-xml-mode
  481. (setq-local skeleton-transformation-function sgml-transformation-function))
  482. ;; This will allow existing comments within declarations to be
  483. ;; recognized.
  484. ;; I can't find a clear description of SGML/XML comments, but it seems that
  485. ;; the only reliable ones are <!-- ... --> although it's not clear what
  486. ;; "..." can contain. It used to accept -- ... -- as well, but that was
  487. ;; apparently a mistake.
  488. (setq-local comment-start-skip "<!--[ \t]*")
  489. (setq-local comment-end-skip "[ \t]*--[ \t\n]*>")
  490. ;; This definition has an HTML leaning but probably fits well for other modes.
  491. (setq imenu-generic-expression
  492. `((nil
  493. ,(concat "<!\\(element\\|entity\\)[ \t\n]+%?[ \t\n]*\\("
  494. sgml-name-re "\\)")
  495. 2)
  496. ("Id"
  497. ,(concat "<[^>]+[ \t\n]+[Ii][Dd]=\\(['\"]"
  498. (if sgml-xml-mode "" "?")
  499. "\\)\\(" sgml-name-re "\\)\\1")
  500. 2)
  501. ("Name"
  502. ,(concat "<[^>]+[ \t\n]+[Nn][Aa][Mm][Ee]=\\(['\"]"
  503. (if sgml-xml-mode "" "?")
  504. "\\)\\(" sgml-name-re "\\)\\1")
  505. 2))))
  506. (defun sgml-comment-indent ()
  507. (if (looking-at "--") comment-column 0))
  508. (defun sgml-slash (arg)
  509. "Insert ARG slash characters.
  510. Behaves electrically if `sgml-quick-keys' is non-nil."
  511. (interactive "p")
  512. (cond
  513. ((not (and (eq (char-before) ?<) (= arg 1)))
  514. (sgml-slash-matching arg))
  515. ((eq sgml-quick-keys 'indent)
  516. (insert-char ?/ 1)
  517. (indent-according-to-mode))
  518. ((eq sgml-quick-keys 'close)
  519. (delete-char -1)
  520. (sgml-close-tag))
  521. (t
  522. (sgml-slash-matching arg))))
  523. (defun sgml-slash-matching (arg)
  524. "Insert `/' and display any previous matching `/'.
  525. Two `/'s are treated as matching if the first `/' ends a net-enabling
  526. start tag, and the second `/' is the corresponding null end tag."
  527. (interactive "p")
  528. (insert-char ?/ arg)
  529. (if (> arg 0)
  530. (let ((oldpos (point))
  531. (blinkpos)
  532. (level 0))
  533. (save-excursion
  534. (save-restriction
  535. (if sgml-slash-distance
  536. (narrow-to-region (max (point-min)
  537. (- (point) sgml-slash-distance))
  538. oldpos))
  539. (if (and (re-search-backward sgml-start-tag-regex (point-min) t)
  540. (eq (match-end 0) (1- oldpos)))
  541. ()
  542. (goto-char (1- oldpos))
  543. (while (and (not blinkpos)
  544. (search-backward "/" (point-min) t))
  545. (let ((tagend (save-excursion
  546. (if (re-search-backward sgml-start-tag-regex
  547. (point-min) t)
  548. (match-end 0)
  549. nil))))
  550. (if (eq tagend (point))
  551. (if (eq level 0)
  552. (setq blinkpos (point))
  553. (setq level (1- level)))
  554. (setq level (1+ level)))))))
  555. (when blinkpos
  556. (goto-char blinkpos)
  557. (if (pos-visible-in-window-p)
  558. (sit-for 1)
  559. (message "Matches %s"
  560. (buffer-substring (line-beginning-position)
  561. (1+ blinkpos)))))))))
  562. ;; Why doesn't this use the iso-cvt table or, preferably, generate the
  563. ;; inverse of the extensive table in the SGML Quail input method? -- fx
  564. ;; I guess that's moot since it only works with Latin-1 anyhow.
  565. (defun sgml-name-char (&optional char)
  566. "Insert a symbolic character name according to `sgml-char-names'.
  567. Non-ASCII chars may be inserted either with the meta key, as in M-SPC for
  568. no-break space or M-- for a soft hyphen; or via an input method or
  569. encoded keyboard operation."
  570. (interactive "*")
  571. (insert ?&)
  572. (or char
  573. (setq char (read-quoted-char "Enter char or octal number")))
  574. (delete-char -1)
  575. (insert char)
  576. (undo-boundary)
  577. (sgml-namify-char))
  578. (defun sgml-namify-char ()
  579. "Change the char before point into its `&name;' equivalent.
  580. Uses `sgml-char-names'."
  581. (interactive)
  582. (let* ((char (char-before))
  583. (name
  584. (cond
  585. ((null char) (error "No char before point"))
  586. ((< char 256) (or (aref sgml-char-names char) char))
  587. ((aref sgml-char-names-table char))
  588. ((encode-char char 'ucs)))))
  589. (if (not name)
  590. (error "Don't know the name of `%c'" char)
  591. (delete-char -1)
  592. (insert (format (if (numberp name) "&#%d;" "&%s;") name)))))
  593. (defun sgml-name-self ()
  594. "Insert a symbolic character name according to `sgml-char-names'."
  595. (interactive "*")
  596. (sgml-name-char last-command-event))
  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. (sgml-name-char last-command-event)
  602. (self-insert-command 1)))
  603. (defun sgml-name-8bit-mode ()
  604. "Toggle whether to insert named entities instead of non-ASCII characters.
  605. This only works for Latin-1 input."
  606. (interactive)
  607. (setq sgml-name-8bit-mode (not sgml-name-8bit-mode))
  608. (message "sgml name entity mode is now %s"
  609. (if sgml-name-8bit-mode "ON" "OFF")))
  610. ;; When an element of a skeleton is a string "str", it is passed
  611. ;; through `skeleton-transformation-function' and inserted.
  612. ;; If "str" is to be inserted literally, one should obtain it as
  613. ;; the return value of a function, e.g. (identity "str").
  614. (defvar sgml-tag-last nil)
  615. (defvar sgml-tag-history nil)
  616. (define-skeleton sgml-tag
  617. "Prompt for a tag and insert it, optionally with attributes.
  618. Completion and configuration are done according to `sgml-tag-alist'.
  619. If you like tags and attributes in uppercase, customize
  620. `sgml-transformation-function' to 'upcase."
  621. (funcall (or skeleton-transformation-function 'identity)
  622. (setq sgml-tag-last
  623. (completing-read
  624. (if (> (length sgml-tag-last) 0)
  625. (format "Tag (default %s): " sgml-tag-last)
  626. "Tag: ")
  627. sgml-tag-alist nil nil nil 'sgml-tag-history sgml-tag-last)))
  628. ?< str |
  629. (("") -1 '(undo-boundary) (identity "&lt;")) | ; see comment above
  630. `(("") '(setq v2 (sgml-attributes ,str t)) ?>
  631. (cond
  632. ((string= "![" ,str)
  633. (backward-char)
  634. '(("") " [ " _ " ]]"))
  635. ((and (eq v2 t) sgml-xml-mode (member ,str sgml-empty-tags))
  636. '(("") -1 " />"))
  637. ((or (and (eq v2 t) (not sgml-xml-mode)) (string-match "^[/!?]" ,str))
  638. nil)
  639. ((symbolp v2)
  640. ;; Make sure we don't fall into an infinite loop.
  641. ;; For xhtml's `tr' tag, we should maybe use \n instead.
  642. (if (eq v2 t) (setq v2 nil))
  643. ;; We use `identity' to prevent skeleton from passing
  644. ;; `str' through `skeleton-transformation-function' a second time.
  645. '(("") v2 _ v2 "</" (identity ',str) ?> >))
  646. ((eq (car v2) t)
  647. (cons '("") (cdr v2)))
  648. (t
  649. (append '(("") (car v2))
  650. (cdr v2)
  651. '(resume: (car v2) _ "</" (identity ',str) ?> >))))))
  652. (autoload 'skeleton-read "skeleton")
  653. (defun sgml-attributes (tag &optional quiet)
  654. "When at top level of a tag, interactively insert attributes.
  655. Completion and configuration of TAG are done according to `sgml-tag-alist'.
  656. If QUIET, do not print a message when there are no attributes for TAG."
  657. (interactive (list (save-excursion (sgml-beginning-of-tag t))))
  658. (or (stringp tag) (error "Wrong context for adding attribute"))
  659. (if tag
  660. (let ((completion-ignore-case t)
  661. (alist (cdr (assoc (downcase tag) sgml-tag-alist)))
  662. car attribute i)
  663. (if (or (symbolp (car alist))
  664. (symbolp (car (car alist))))
  665. (setq car (car alist)
  666. alist (cdr alist)))
  667. (or quiet
  668. (message "No attributes configured."))
  669. (if (stringp (car alist))
  670. (progn
  671. (insert (if (eq (preceding-char) ?\s) "" ?\s)
  672. (funcall skeleton-transformation-function (car alist)))
  673. (sgml-value alist))
  674. (setq i (length alist))
  675. (while (> i 0)
  676. (insert ?\s)
  677. (insert (funcall skeleton-transformation-function
  678. (setq attribute
  679. (skeleton-read (lambda ()
  680. (completing-read
  681. "Attribute: "
  682. alist))))))
  683. (if (string= "" attribute)
  684. (setq i 0)
  685. (sgml-value (assoc (downcase attribute) alist))
  686. (setq i (1- i))))
  687. (if (eq (preceding-char) ?\s)
  688. (delete-char -1)))
  689. car)))
  690. (defun sgml-auto-attributes (arg)
  691. "Self insert the character typed; at top level of tag, prompt for attributes.
  692. With prefix argument, only self insert."
  693. (interactive "*P")
  694. (let ((point (point))
  695. tag)
  696. (if (or arg
  697. (not sgml-tag-alist) ; no message when nothing configured
  698. (symbolp (setq tag (save-excursion (sgml-beginning-of-tag t))))
  699. (eq (aref tag 0) ?/))
  700. (self-insert-command (prefix-numeric-value arg))
  701. (sgml-attributes tag)
  702. (setq last-command-event ?\s)
  703. (or (> (point) point)
  704. (self-insert-command 1)))))
  705. (defun sgml-tag-help (&optional tag)
  706. "Display description of tag TAG. If TAG is omitted, use the tag at point."
  707. (interactive
  708. (list (let ((def (save-excursion
  709. (if (eq (following-char) ?<) (forward-char))
  710. (sgml-beginning-of-tag))))
  711. (completing-read (if def
  712. (format "Tag (default %s): " def)
  713. "Tag: ")
  714. sgml-tag-alist nil nil nil
  715. 'sgml-tag-history def))))
  716. (or (and tag (> (length tag) 0))
  717. (save-excursion
  718. (if (eq (following-char) ?<)
  719. (forward-char))
  720. (setq tag (sgml-beginning-of-tag))))
  721. (or (stringp tag)
  722. (error "No tag selected"))
  723. (setq tag (downcase tag))
  724. (message "%s"
  725. (or (cdr (assoc (downcase tag) sgml-tag-help))
  726. (and (eq (aref tag 0) ?/)
  727. (cdr (assoc (downcase (substring tag 1)) sgml-tag-help)))
  728. "No description available")))
  729. (defun sgml-maybe-end-tag (&optional arg)
  730. "Name self unless in position to end a tag or a prefix ARG is given."
  731. (interactive "P")
  732. (if (or arg (eq (car (sgml-lexical-context)) 'tag))
  733. (self-insert-command (prefix-numeric-value arg))
  734. (sgml-name-self)))
  735. (defun sgml-skip-tag-backward (arg)
  736. "Skip to beginning of tag or matching opening tag if present.
  737. With prefix argument ARG, repeat this ARG times.
  738. Return non-nil if we skipped over matched tags."
  739. (interactive "p")
  740. ;; FIXME: use sgml-get-context or something similar.
  741. (let ((return t))
  742. (while (>= arg 1)
  743. (search-backward "<" nil t)
  744. (if (looking-at "</\\([^ \n\t>]+\\)")
  745. ;; end tag, skip any nested pairs
  746. (let ((case-fold-search t)
  747. (re (concat "</?" (regexp-quote (match-string 1))
  748. ;; Ignore empty tags like <foo/>.
  749. "\\([^>]*[^/>]\\)?>")))
  750. (while (and (re-search-backward re nil t)
  751. (eq (char-after (1+ (point))) ?/))
  752. (forward-char 1)
  753. (sgml-skip-tag-backward 1)))
  754. (setq return nil))
  755. (setq arg (1- arg)))
  756. return))
  757. (defvar sgml-electric-tag-pair-overlays nil)
  758. (defvar sgml-electric-tag-pair-timer nil)
  759. (defun sgml-electric-tag-pair-before-change-function (_beg end)
  760. (condition-case err
  761. (save-excursion
  762. (goto-char end)
  763. (skip-chars-backward "[:alnum:]-_.:")
  764. (if (and ;; (<= (point) beg) ; This poses problems for downcase-word.
  765. (or (eq (char-before) ?<)
  766. (and (eq (char-before) ?/)
  767. (eq (char-before (1- (point))) ?<)))
  768. (null (get-char-property (point) 'text-clones)))
  769. (let* ((endp (eq (char-before) ?/))
  770. (cl-start (point))
  771. (cl-end (progn (skip-chars-forward "[:alnum:]-_.:") (point)))
  772. (match
  773. (if endp
  774. (when (sgml-skip-tag-backward 1) (forward-char 1) t)
  775. (with-syntax-table sgml-tag-syntax-table
  776. (up-list -1)
  777. (when (sgml-skip-tag-forward 1)
  778. (backward-sexp 1)
  779. (forward-char 2)
  780. t))))
  781. (clones (get-char-property (point) 'text-clones)))
  782. (when (and match
  783. (/= cl-end cl-start)
  784. (equal (buffer-substring cl-start cl-end)
  785. (buffer-substring (point)
  786. (save-excursion
  787. (skip-chars-forward "[:alnum:]-_.:")
  788. (point))))
  789. (or (not endp) (eq (char-after cl-end) ?>)))
  790. (when clones
  791. (message "sgml-electric-tag-pair-before-change-function: deleting old OLs")
  792. (mapc 'delete-overlay clones))
  793. (message "sgml-electric-tag-pair-before-change-function: new clone")
  794. (text-clone-create cl-start cl-end 'spread "[[:alnum:]-_.:]+")
  795. (setq sgml-electric-tag-pair-overlays
  796. (append (get-char-property (point) 'text-clones)
  797. sgml-electric-tag-pair-overlays))))))
  798. (scan-error nil)
  799. (error (message "Error in sgml-electric-pair-mode: %s" err))))
  800. (defun sgml-electric-tag-pair-flush-overlays ()
  801. (while sgml-electric-tag-pair-overlays
  802. (delete-overlay (pop sgml-electric-tag-pair-overlays))))
  803. (define-minor-mode sgml-electric-tag-pair-mode
  804. "Toggle SGML Electric Tag Pair mode.
  805. With a prefix argument ARG, enable the mode if ARG is positive,
  806. and disable it otherwise. If called from Lisp, enable the mode
  807. if ARG is omitted or nil.
  808. SGML Electric Tag Pair mode is a buffer-local minor mode for use
  809. with `sgml-mode' and related major modes. When enabled, editing
  810. an opening markup tag automatically updates the closing tag."
  811. :lighter "/e"
  812. (if sgml-electric-tag-pair-mode
  813. (progn
  814. (add-hook 'before-change-functions
  815. 'sgml-electric-tag-pair-before-change-function
  816. nil t)
  817. (unless sgml-electric-tag-pair-timer
  818. (setq sgml-electric-tag-pair-timer
  819. (run-with-idle-timer 5 'repeat 'sgml-electric-tag-pair-flush-overlays))))
  820. (remove-hook 'before-change-functions
  821. 'sgml-electric-tag-pair-before-change-function
  822. t)
  823. ;; We leave the timer running for other buffers.
  824. ))
  825. (defun sgml-skip-tag-forward (arg)
  826. "Skip to end of tag or matching closing tag if present.
  827. With prefix argument ARG, repeat this ARG times.
  828. Return t if after a closing tag."
  829. (interactive "p")
  830. ;; FIXME: Use sgml-get-context or something similar.
  831. ;; It currently might jump to an unrelated </P> if the <P>
  832. ;; we're skipping has no matching </P>.
  833. (let ((return t))
  834. (with-syntax-table sgml-tag-syntax-table
  835. (while (>= arg 1)
  836. (skip-chars-forward "^<>")
  837. (if (eq (following-char) ?>)
  838. (up-list -1))
  839. (if (looking-at "<\\([^/ \n\t>]+\\)\\([^>]*[^/>]\\)?>")
  840. ;; start tag, skip any nested same pairs _and_ closing tag
  841. (let ((case-fold-search t)
  842. (re (concat "</?" (regexp-quote (match-string 1))
  843. ;; Ignore empty tags like <foo/>.
  844. "\\([^>]*[^/>]\\)?>"))
  845. point close)
  846. (forward-list 1)
  847. (setq point (point))
  848. ;; FIXME: This re-search-forward will mistakenly match
  849. ;; tag-like text inside attributes.
  850. (while (and (re-search-forward re nil t)
  851. (not (setq close
  852. (eq (char-after (1+ (match-beginning 0))) ?/)))
  853. (goto-char (match-beginning 0))
  854. (sgml-skip-tag-forward 1))
  855. (setq close nil))
  856. (unless close
  857. (goto-char point)
  858. (setq return nil)))
  859. (forward-list 1))
  860. (setq arg (1- arg)))
  861. return)))
  862. (defsubst sgml-looking-back-at (str)
  863. "Return t if the test before point matches STR."
  864. (let ((start (- (point) (length str))))
  865. (and (>= start (point-min))
  866. (equal str (buffer-substring-no-properties start (point))))))
  867. (defun sgml-delete-tag (arg)
  868. ;; FIXME: Should be called sgml-kill-tag or should not touch the kill-ring.
  869. "Delete tag on or after cursor, and matching closing or opening tag.
  870. With prefix argument ARG, repeat this ARG times."
  871. (interactive "p")
  872. (while (>= arg 1)
  873. (save-excursion
  874. (let* (close open)
  875. (if (looking-at "[ \t\n]*<")
  876. ;; just before tag
  877. (if (eq (char-after (match-end 0)) ?/)
  878. ;; closing tag
  879. (progn
  880. (setq close (point))
  881. (goto-char (match-end 0))))
  882. ;; on tag?
  883. (or (save-excursion (setq close (sgml-beginning-of-tag)
  884. close (and (stringp close)
  885. (eq (aref close 0) ?/)
  886. (point))))
  887. ;; not on closing tag
  888. (let ((point (point)))
  889. (sgml-skip-tag-backward 1)
  890. (if (or (not (eq (following-char) ?<))
  891. (save-excursion
  892. (forward-list 1)
  893. (<= (point) point)))
  894. (error "Not on or before tag")))))
  895. (if close
  896. (progn
  897. (sgml-skip-tag-backward 1)
  898. (setq open (point))
  899. (goto-char close)
  900. (kill-sexp 1))
  901. (setq open (point))
  902. (when (and (sgml-skip-tag-forward 1)
  903. (not (sgml-looking-back-at "/>")))
  904. (kill-sexp -1)))
  905. ;; Delete any resulting empty line. If we didn't kill-sexp,
  906. ;; this *should* do nothing, because we're right after the tag.
  907. (if (progn (forward-line 0) (looking-at "\\(?:[ \t]*$\\)\n?"))
  908. (delete-region (match-beginning 0) (match-end 0)))
  909. (goto-char open)
  910. (kill-sexp 1)
  911. (if (progn (forward-line 0) (looking-at "\\(?:[ \t]*$\\)\n?"))
  912. (delete-region (match-beginning 0) (match-end 0)))))
  913. (setq arg (1- arg))))
  914. ;; Put read-only last to enable setting this even when read-only enabled.
  915. (or (get 'sgml-tag 'invisible)
  916. (setplist 'sgml-tag
  917. (append '(invisible t
  918. cursor-sensor-functions (sgml-cursor-sensor)
  919. rear-nonsticky t
  920. read-only t)
  921. (symbol-plist 'sgml-tag))))
  922. (defun sgml-tags-invisible (arg)
  923. "Toggle visibility of existing tags."
  924. (interactive "P")
  925. (let ((inhibit-read-only t)
  926. string)
  927. (with-silent-modifications
  928. (save-excursion
  929. (goto-char (point-min))
  930. (if (setq-local sgml-tags-invisible
  931. (if arg
  932. (>= (prefix-numeric-value arg) 0)
  933. (not sgml-tags-invisible)))
  934. (while (re-search-forward sgml-tag-name-re nil t)
  935. (setq string
  936. (cdr (assq (intern-soft (downcase (match-string 1)))
  937. sgml-display-text)))
  938. (goto-char (match-beginning 0))
  939. (and (stringp string)
  940. (not (overlays-at (point)))
  941. (let ((ol (make-overlay (point) (match-beginning 1))))
  942. (overlay-put ol 'before-string string)
  943. (overlay-put ol 'sgml-tag t)))
  944. (put-text-property (point)
  945. (progn (forward-list) (point))
  946. 'category 'sgml-tag))
  947. (let ((pos (point-min)))
  948. (while (< (setq pos (next-overlay-change pos)) (point-max))
  949. (dolist (ol (overlays-at pos))
  950. (if (overlay-get ol 'sgml-tag)
  951. (delete-overlay ol)))))
  952. (remove-text-properties (point-min) (point-max) '(category nil)))))
  953. (cursor-sensor-mode (if sgml-tags-invisible 1 -1))
  954. (run-hooks 'sgml-tags-invisible-hook)
  955. (message "")))
  956. (defun sgml-cursor-sensor (window x dir)
  957. ;; Show preceding or following hidden tag, depending of cursor direction (and
  958. ;; `dir' is not the direction in this sense).
  959. (when (eq dir 'entered)
  960. (ignore-errors
  961. (let* ((y (window-point window))
  962. (otherend
  963. (save-excursion
  964. (goto-char y)
  965. (cond
  966. ((and (eq (char-before) ?>)
  967. (or (not (eq (char-after) ?<))
  968. (> x y)))
  969. (backward-sexp))
  970. ((eq (char-after y) ?<)
  971. (forward-sexp)))
  972. (point))))
  973. (message "Invisible tag: %s"
  974. ;; Strip properties, otherwise, the text is invisible.
  975. (buffer-substring-no-properties
  976. y otherend))))))
  977. (defun sgml-validate (command)
  978. "Validate an SGML document.
  979. Runs COMMAND, a shell command, in a separate process asynchronously
  980. with output going to the buffer `*compilation*'.
  981. You can then use the command \\[next-error] to find the next error message
  982. and move to the line in the SGML document that caused it."
  983. (interactive
  984. (list (read-string "Validate command: "
  985. (or sgml-saved-validate-command
  986. (concat sgml-validate-command
  987. " "
  988. (shell-quote-argument
  989. (let ((name (buffer-file-name)))
  990. (and name
  991. (file-name-nondirectory name)))))))))
  992. (setq sgml-saved-validate-command command)
  993. (save-some-buffers (not compilation-ask-about-save) nil)
  994. (compilation-start command))
  995. (defsubst sgml-at-indentation-p ()
  996. "Return true if point is at the first non-whitespace character on the line."
  997. (save-excursion
  998. (skip-chars-backward " \t")
  999. (bolp)))
  1000. (defun sgml-lexical-context (&optional limit)
  1001. "Return the lexical context at point as (TYPE . START).
  1002. START is the location of the start of the lexical element.
  1003. TYPE is one of `string', `comment', `tag', `cdata', `pi', or `text'.
  1004. Optional argument LIMIT is the position to start parsing from.
  1005. If nil, start from a preceding tag at indentation."
  1006. (save-excursion
  1007. (let ((pos (point))
  1008. text-start state)
  1009. (if limit
  1010. (goto-char limit)
  1011. ;; Skip tags backwards until we find one at indentation
  1012. (while (and (ignore-errors (sgml-parse-tag-backward))
  1013. (not (sgml-at-indentation-p)))))
  1014. (with-syntax-table sgml-tag-syntax-table
  1015. (while (< (point) pos)
  1016. ;; When entering this loop we're inside text.
  1017. (setq text-start (point))
  1018. (skip-chars-forward "^<" pos)
  1019. (setq state
  1020. (cond
  1021. ((= (point) pos)
  1022. ;; We got to the end without seeing a tag.
  1023. nil)
  1024. ((looking-at "<!\\[[A-Z]+\\[")
  1025. ;; We've found a CDATA section or similar.
  1026. (let ((cdata-start (point)))
  1027. (unless (search-forward "]]>" pos 'move)
  1028. (list 0 nil nil 'cdata nil nil nil nil cdata-start))))
  1029. ((looking-at comment-start-skip)
  1030. ;; parse-partial-sexp doesn't handle <!-- comments -->,
  1031. ;; or only if ?- is in sgml-specials, so match explicitly
  1032. (let ((start (point)))
  1033. (unless (re-search-forward comment-end-skip pos 'move)
  1034. (list 0 nil nil nil t nil nil nil start))))
  1035. ((and sgml-xml-mode (looking-at "<\\?"))
  1036. ;; Processing Instructions.
  1037. ;; In SGML, it's basically a normal tag of the form
  1038. ;; <?NAME ...> but in XML, it takes the form <? ... ?>.
  1039. (let ((pi-start (point)))
  1040. (unless (search-forward "?>" pos 'move)
  1041. (list 0 nil nil 'pi nil nil nil nil pi-start))))
  1042. (t
  1043. ;; We've reached a tag. Parse it.
  1044. ;; FIXME: Handle net-enabling start-tags
  1045. (parse-partial-sexp (point) pos 0))))))
  1046. (cond
  1047. ((memq (nth 3 state) '(cdata pi)) (cons (nth 3 state) (nth 8 state)))
  1048. ((nth 3 state) (cons 'string (nth 8 state)))
  1049. ((nth 4 state) (cons 'comment (nth 8 state)))
  1050. ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state)))
  1051. (t (cons 'text text-start))))))
  1052. (defun sgml-beginning-of-tag (&optional only-immediate)
  1053. "Skip to beginning of tag and return its name.
  1054. If this can't be done, return nil."
  1055. (let ((context (sgml-lexical-context)))
  1056. (if (eq (car context) 'tag)
  1057. (progn
  1058. (goto-char (cdr context))
  1059. (when (looking-at sgml-tag-name-re)
  1060. (match-string-no-properties 1)))
  1061. (if only-immediate nil
  1062. (when (not (eq (car context) 'text))
  1063. (goto-char (cdr context))
  1064. (sgml-beginning-of-tag t))))))
  1065. (defun sgml-value (alist)
  1066. "Interactively insert value taken from attribute-rule ALIST.
  1067. See `sgml-tag-alist' for info about attribute rules."
  1068. (setq alist (cdr alist))
  1069. (if (stringp (car alist))
  1070. (insert "=\"" (car alist) ?\")
  1071. (if (and (eq (car alist) t) (not sgml-xml-mode))
  1072. (when (cdr alist)
  1073. (insert "=\"")
  1074. (setq alist (skeleton-read (lambda ()
  1075. (completing-read
  1076. "Value: " (cdr alist)))))
  1077. (if (string< "" alist)
  1078. (insert alist ?\")
  1079. (delete-char -2)))
  1080. (insert "=\"")
  1081. (if (cdr alist)
  1082. (insert (skeleton-read (lambda ()
  1083. (completing-read "Value: " alist))))
  1084. (when (null alist)
  1085. (insert (skeleton-read '(read-string "Value: ")))))
  1086. (insert ?\"))))
  1087. (defun sgml-quote (start end &optional unquotep)
  1088. "Quote SGML text in region START ... END.
  1089. Only &, < and > are quoted, the rest is left untouched.
  1090. With prefix argument UNQUOTEP, unquote the region."
  1091. (interactive "r\nP")
  1092. (save-restriction
  1093. (narrow-to-region start end)
  1094. (goto-char (point-min))
  1095. (if unquotep
  1096. ;; FIXME: We should unquote other named character references as well.
  1097. (while (re-search-forward
  1098. "\\(&\\(amp\\|\\(l\\|\\(g\\)\\)t\\)\\)[][<>&;\n\t \"%!'(),/=?]"
  1099. nil t)
  1100. (replace-match (if (match-end 4) ">" (if (match-end 3) "<" "&")) t t
  1101. nil (if (eq (char-before (match-end 0)) ?\;) 0 1)))
  1102. (while (re-search-forward "[&<>]" nil t)
  1103. (replace-match (cdr (assq (char-before) '((?& . "&amp;")
  1104. (?< . "&lt;")
  1105. (?> . "&gt;"))))
  1106. t t)))))
  1107. (defun sgml-pretty-print (beg end)
  1108. "Simple-minded pretty printer for SGML.
  1109. Re-indents the code and inserts newlines between BEG and END.
  1110. You might want to turn on `auto-fill-mode' to get better results."
  1111. ;; TODO:
  1112. ;; - insert newline between some start-tag and text.
  1113. ;; - don't insert newline in front of some end-tags.
  1114. (interactive "r")
  1115. (save-excursion
  1116. (if (< beg end)
  1117. (goto-char beg)
  1118. (goto-char end)
  1119. (setq end beg)
  1120. (setq beg (point)))
  1121. ;; Don't use narrowing because it screws up auto-indent.
  1122. (setq end (copy-marker end t))
  1123. (with-syntax-table sgml-tag-syntax-table
  1124. (while (re-search-forward "<" end t)
  1125. (goto-char (match-beginning 0))
  1126. (unless (or ;;(looking-at "</")
  1127. (progn (skip-chars-backward " \t") (bolp)))
  1128. (reindent-then-newline-and-indent))
  1129. (forward-sexp 1)))
  1130. ;; (indent-region beg end)
  1131. ))
  1132. ;; Parsing
  1133. (cl-defstruct (sgml-tag
  1134. (:constructor sgml-make-tag (type start end name)))
  1135. type start end name)
  1136. (defsubst sgml-parse-tag-name ()
  1137. "Skip past a tag-name, and return the name."
  1138. (buffer-substring-no-properties
  1139. (point) (progn (skip-syntax-forward "w_") (point))))
  1140. (defun sgml-tag-text-p (start end)
  1141. "Return non-nil if text between START and END is a tag.
  1142. Checks among other things that the tag does not contain spurious
  1143. unquoted < or > chars inside, which would indicate that it
  1144. really isn't a tag after all."
  1145. (save-excursion
  1146. (with-syntax-table sgml-tag-syntax-table
  1147. (let ((pps (parse-partial-sexp start end 2)))
  1148. (and (= (nth 0 pps) 0))))))
  1149. (defun sgml-parse-tag-backward (&optional limit)
  1150. "Parse an SGML tag backward, and return information about the tag.
  1151. Assume that parsing starts from within a textual context.
  1152. Leave point at the beginning of the tag."
  1153. (catch 'found
  1154. (let (tag-type tag-start tag-end name)
  1155. (or (re-search-backward "[<>]" limit 'move)
  1156. (error "No tag found"))
  1157. (when (eq (char-after) ?<)
  1158. ;; Oops!! Looks like we were not in a textual context after all!.
  1159. ;; Let's try to recover.
  1160. ;; Remember the tag-start so we don't need to look for it later.
  1161. ;; This is not just an optimization but also makes sure we don't get
  1162. ;; stuck in infloops in cases where "looking back for <" would not go
  1163. ;; back far enough.
  1164. (setq tag-start (point))
  1165. (with-syntax-table sgml-tag-syntax-table
  1166. (let ((pos (point)))
  1167. (condition-case nil
  1168. ;; FIXME: This does not correctly skip over PI an CDATA tags.
  1169. (forward-sexp)
  1170. (scan-error
  1171. ;; This < seems to be just a spurious one, let's ignore it.
  1172. (goto-char pos)
  1173. (throw 'found (sgml-parse-tag-backward limit))))
  1174. ;; Check it is really a tag, without any extra < or > inside.
  1175. (unless (sgml-tag-text-p pos (point))
  1176. (goto-char pos)
  1177. (throw 'found (sgml-parse-tag-backward limit)))
  1178. (forward-char -1))))
  1179. (setq tag-end (1+ (point)))
  1180. (cond
  1181. ((sgml-looking-back-at "--") ; comment
  1182. (setq tag-type 'comment
  1183. tag-start (or tag-start (search-backward "<!--" nil t))))
  1184. ((sgml-looking-back-at "]]") ; cdata
  1185. (setq tag-type 'cdata
  1186. tag-start (or tag-start
  1187. (re-search-backward "<!\\[[A-Z]+\\[" nil t))))
  1188. ((sgml-looking-back-at "?") ; XML processing-instruction
  1189. (setq tag-type 'pi
  1190. ;; IIUC: SGML processing instructions take the form <?foo ...>
  1191. ;; i.e. a "normal" tag, handled below. In XML this is changed
  1192. ;; to <?foo ... ?> where "..." can contain < and > and even <?
  1193. ;; but not ?>. This means that when parsing backward, there's
  1194. ;; no easy way to make sure that we find the real beginning of
  1195. ;; the PI.
  1196. tag-start (or tag-start (search-backward "<?" nil t))))
  1197. (t
  1198. (unless tag-start
  1199. (setq tag-start
  1200. (with-syntax-table sgml-tag-syntax-table
  1201. (goto-char tag-end)
  1202. (condition-case nil
  1203. (backward-sexp)
  1204. (scan-error
  1205. ;; This > isn't really the end of a tag. Skip it.
  1206. (goto-char (1- tag-end))
  1207. (throw 'found (sgml-parse-tag-backward limit))))
  1208. (point))))
  1209. (goto-char (1+ tag-start))
  1210. (pcase (char-after)
  1211. (?! (setq tag-type 'decl)) ; declaration
  1212. (?? (setq tag-type 'pi)) ; processing-instruction
  1213. (?% (setq tag-type 'jsp)) ; JSP tags
  1214. (?/ ; close-tag
  1215. (forward-char 1)
  1216. (setq tag-type 'close
  1217. name (sgml-parse-tag-name)))
  1218. (_ ; open or empty tag
  1219. (setq tag-type 'open
  1220. name (sgml-parse-tag-name))
  1221. (if (or (eq ?/ (char-before (- tag-end 1)))
  1222. (sgml-empty-tag-p name))
  1223. (setq tag-type 'empty))))))
  1224. (goto-char tag-start)
  1225. (sgml-make-tag tag-type tag-start tag-end name))))
  1226. (defun sgml-get-context (&optional until)
  1227. "Determine the context of the current position.
  1228. By default, parse until we find a start-tag as the first thing on a line.
  1229. If UNTIL is `empty', return even if the context is empty (i.e.
  1230. we just skipped over some element and got to a beginning of line).
  1231. The context is a list of tag-info structures. The last one is the tag
  1232. immediately enclosing the current position.
  1233. Point is assumed to be outside of any tag. If we discover that it's
  1234. not the case, the first tag returned is the one inside which we are."
  1235. (let ((here (point))
  1236. (stack nil)
  1237. (ignore nil)
  1238. (context nil)
  1239. tag-info)
  1240. ;; CONTEXT keeps track of the tag-stack
  1241. ;; STACK keeps track of the end tags we've seen (and thus the start-tags
  1242. ;; we'll have to ignore) when skipping over matching open..close pairs.
  1243. ;; IGNORE is a list of tags that can be ignored because they have been
  1244. ;; closed implicitly.
  1245. (skip-chars-backward " \t\n") ; Make sure we're not at indentation.
  1246. (while
  1247. (and (not (eq until 'now))
  1248. (or stack
  1249. (not (if until (eq until 'empty) context))
  1250. (not (sgml-at-indentation-p))
  1251. (and context
  1252. (/= (point) (sgml-tag-start (car context)))
  1253. (sgml-unclosed-tag-p (sgml-tag-name (car context)))))
  1254. (setq tag-info (ignore-errors (sgml-parse-tag-backward))))
  1255. ;; This tag may enclose things we thought were tags. If so,
  1256. ;; discard them.
  1257. (while (and context
  1258. (> (sgml-tag-end tag-info)
  1259. (sgml-tag-end (car context))))
  1260. (setq context (cdr context)))
  1261. (cond
  1262. ((> (sgml-tag-end tag-info) here)
  1263. ;; Oops!! Looks like we were not outside of any tag, after all.
  1264. (push tag-info context)
  1265. (setq until 'now))
  1266. ;; start-tag
  1267. ((eq (sgml-tag-type tag-info) 'open)
  1268. (cond
  1269. ((null stack)
  1270. (if (assoc-string (sgml-tag-name tag-info) ignore t)
  1271. ;; There was an implicit end-tag.
  1272. nil
  1273. (push tag-info context)
  1274. ;; We're changing context so the tags implicitly closed inside
  1275. ;; the previous context aren't implicitly closed here any more.
  1276. ;; [ Well, actually it depends, but we don't have the info about
  1277. ;; when it doesn't and when it does. --Stef ]
  1278. (setq ignore nil)))
  1279. ((eq t (compare-strings (sgml-tag-name tag-info) nil nil
  1280. (car stack) nil nil t))
  1281. (setq stack (cdr stack)))
  1282. (t
  1283. ;; The open and close tags don't match.
  1284. (if (not sgml-xml-mode)
  1285. (unless (sgml-unclosed-tag-p (sgml-tag-name tag-info))
  1286. (message "Unclosed tag <%s>" (sgml-tag-name tag-info))
  1287. (let ((tmp stack))
  1288. ;; We could just assume that the tag is simply not closed
  1289. ;; but it's a bad assumption when tags *are* closed but
  1290. ;; not properly nested.
  1291. (while (and (cdr tmp)
  1292. (not (eq t (compare-strings
  1293. (sgml-tag-name tag-info) nil nil
  1294. (cadr tmp) nil nil t))))
  1295. (setq tmp (cdr tmp)))
  1296. (if (cdr tmp) (setcdr tmp (cddr tmp)))))
  1297. (message "Unmatched tags <%s> and </%s>"
  1298. (sgml-tag-name tag-info) (pop stack)))))
  1299. (if (and (null stack) (sgml-unclosed-tag-p (sgml-tag-name tag-info)))
  1300. ;; This is a top-level open of an implicitly closed tag, so any
  1301. ;; occurrence of such an open tag at the same level can be ignored
  1302. ;; because it's been implicitly closed.
  1303. (push (sgml-tag-name tag-info) ignore)))
  1304. ;; end-tag
  1305. ((eq (sgml-tag-type tag-info) 'close)
  1306. (if (sgml-empty-tag-p (sgml-tag-name tag-info))
  1307. (message "Spurious </%s>: empty tag" (sgml-tag-name tag-info))
  1308. (push (sgml-tag-name tag-info) stack)))
  1309. ))
  1310. ;; return context
  1311. context))
  1312. (defun sgml-show-context (&optional full)
  1313. "Display the current context.
  1314. If FULL is non-nil, parse back to the beginning of the buffer."
  1315. (interactive "P")
  1316. (with-output-to-temp-buffer "*XML Context*"
  1317. (save-excursion
  1318. (let ((context (sgml-get-context)))
  1319. (when full
  1320. (let ((more nil))
  1321. (while (setq more (sgml-get-context))
  1322. (setq context (nconc more context)))))
  1323. (pp context)))))
  1324. ;; Editing shortcuts
  1325. (defun sgml-close-tag ()
  1326. "Close current element.
  1327. Depending on context, inserts a matching close-tag, or closes
  1328. the current start-tag or the current comment or the current cdata, ..."
  1329. (interactive)
  1330. (pcase (car (sgml-lexical-context))
  1331. (`comment (insert " -->"))
  1332. (`cdata (insert "]]>"))
  1333. (`pi (insert " ?>"))
  1334. (`jsp (insert " %>"))
  1335. (`tag (insert " />"))
  1336. (`text
  1337. (let ((context (save-excursion (sgml-get-context))))
  1338. (if context
  1339. (progn
  1340. (insert "</" (sgml-tag-name (car (last context))) ">")
  1341. (indent-according-to-mode)))))
  1342. (_
  1343. (error "Nothing to close"))))
  1344. (defun sgml-empty-tag-p (tag-name)
  1345. "Return non-nil if TAG-NAME is an implicitly empty tag."
  1346. (and (not sgml-xml-mode)
  1347. (assoc-string tag-name sgml-empty-tags 'ignore-case)))
  1348. (defun sgml-unclosed-tag-p (tag-name)
  1349. "Return non-nil if TAG-NAME is a tag for which an end-tag is optional."
  1350. (and (not sgml-xml-mode)
  1351. (assoc-string tag-name sgml-unclosed-tags 'ignore-case)))
  1352. (defun sgml-calculate-indent (&optional lcon)
  1353. "Calculate the column to which this line should be indented.
  1354. LCON is the lexical context, if any."
  1355. (unless lcon (setq lcon (sgml-lexical-context)))
  1356. ;; Indent comment-start markers inside <!-- just like comment-end markers.
  1357. (if (and (eq (car lcon) 'tag)
  1358. (looking-at "--")
  1359. (save-excursion (goto-char (cdr lcon)) (looking-at "<!--")))
  1360. (setq lcon (cons 'comment (+ (cdr lcon) 2))))
  1361. (pcase (car lcon)
  1362. (`string
  1363. ;; Go back to previous non-empty line.
  1364. (while (and (> (point) (cdr lcon))
  1365. (zerop (forward-line -1))
  1366. (looking-at "[ \t]*$")))
  1367. (if (> (point) (cdr lcon))
  1368. ;; Previous line is inside the string.
  1369. (current-indentation)
  1370. (goto-char (cdr lcon))
  1371. (1+ (current-column))))
  1372. (`comment
  1373. (let ((mark (looking-at "--")))
  1374. ;; Go back to previous non-empty line.
  1375. (while (and (> (point) (cdr lcon))
  1376. (zerop (forward-line -1))
  1377. (or (looking-at "[ \t]*$")
  1378. (if mark (not (looking-at "[ \t]*--"))))))
  1379. (if (> (point) (cdr lcon))
  1380. ;; Previous line is inside the comment.
  1381. (skip-chars-forward " \t")
  1382. (goto-char (cdr lcon))
  1383. ;; Skip `<!' to get to the `--' with which we want to align.
  1384. (search-forward "--")
  1385. (goto-char (match-beginning 0)))
  1386. (when (and (not mark) (looking-at "--"))
  1387. (forward-char 2) (skip-chars-forward " \t"))
  1388. (current-column)))
  1389. ;; We don't know how to indent it. Let's be honest about it.
  1390. (`cdata nil)
  1391. ;; We don't know how to indent it. Let's be honest about it.
  1392. (`pi nil)
  1393. (`tag
  1394. (goto-char (+ (cdr lcon) sgml-attribute-offset))
  1395. (skip-chars-forward "^ \t\n") ;Skip tag name.
  1396. (skip-chars-forward " \t")
  1397. (if (not (eolp))
  1398. (current-column)
  1399. ;; This is the first attribute: indent.
  1400. (goto-char (+ (cdr lcon) sgml-attribute-offset))
  1401. (+ (current-column) sgml-basic-offset)))
  1402. (`text
  1403. (while (looking-at "</")
  1404. (forward-sexp 1)
  1405. (skip-chars-forward " \t"))
  1406. (let* ((here (point))
  1407. (unclosed (and ;; (not sgml-xml-mode)
  1408. (looking-at sgml-tag-name-re)
  1409. (assoc-string (match-string 1)
  1410. sgml-unclosed-tags 'ignore-case)
  1411. (match-string 1)))
  1412. (context
  1413. ;; If possible, align on the previous non-empty text line.
  1414. ;; Otherwise, do a more serious parsing to find the
  1415. ;; tag(s) relative to which we should be indenting.
  1416. (if (and (not unclosed) (skip-chars-backward " \t")
  1417. (< (skip-chars-backward " \t\n") 0)
  1418. (back-to-indentation)
  1419. (> (point) (cdr lcon)))
  1420. nil
  1421. (goto-char here)
  1422. (nreverse (sgml-get-context (if unclosed nil 'empty)))))
  1423. (there (point)))
  1424. ;; Ignore previous unclosed start-tag in context.
  1425. (while (and context unclosed
  1426. (eq t (compare-strings
  1427. (sgml-tag-name (car context)) nil nil
  1428. unclosed nil nil t)))
  1429. (setq context (cdr context)))
  1430. ;; Indent to reflect nesting.
  1431. (cond
  1432. ;; If we were not in a text context after all, let's try again.
  1433. ((and context (> (sgml-tag-end (car context)) here))
  1434. (goto-char here)
  1435. (sgml-calculate-indent
  1436. (cons (if (memq (sgml-tag-type (car context)) '(comment cdata))
  1437. (sgml-tag-type (car context)) 'tag)
  1438. (sgml-tag-start (car context)))))
  1439. ;; Align on the first element after the nearest open-tag, if any.
  1440. ((and context
  1441. (goto-char (sgml-tag-end (car context)))
  1442. (skip-chars-forward " \t\n")
  1443. (< (point) here) (sgml-at-indentation-p))
  1444. (current-column))
  1445. ;; ;; If the parsing failed, try to recover.
  1446. ;; ((and (null context) (bobp)
  1447. ;; (not (eq (char-after here) ?<)))
  1448. ;; (goto-char here)
  1449. ;; (if (and (looking-at "--[ \t\n]*>")
  1450. ;; (re-search-backward "<!--" nil t))
  1451. ;; ;; No wonder parsing failed: we're in a comment.
  1452. ;; (sgml-calculate-indent (prog2 (goto-char (match-end 0))
  1453. ;; (sgml-lexical-context)
  1454. ;; (goto-char here)))
  1455. ;; ;; We have no clue what's going on, let's be honest about it.
  1456. ;; nil))
  1457. ;; Otherwise, just follow the rules.
  1458. (t
  1459. (goto-char there)
  1460. (+ (current-column)
  1461. (* sgml-basic-offset (length context)))))))
  1462. (_
  1463. (error "Unrecognized context %s" (car lcon)))
  1464. ))
  1465. (defun sgml-indent-line ()
  1466. "Indent the current line as SGML."
  1467. (interactive)
  1468. (let* ((savep (point))
  1469. (indent-col
  1470. (save-excursion
  1471. (back-to-indentation)
  1472. (if (>= (point) savep) (setq savep nil))
  1473. (sgml-calculate-indent))))
  1474. (if (null indent-col)
  1475. 'noindent
  1476. (if savep
  1477. (save-excursion (indent-line-to indent-col))
  1478. (indent-line-to indent-col)))))
  1479. (defun sgml-guess-indent ()
  1480. "Guess an appropriate value for `sgml-basic-offset'.
  1481. Base the guessed indentation level on the first indented tag in the buffer.
  1482. Add this to `sgml-mode-hook' for convenience."
  1483. (interactive)
  1484. (save-excursion
  1485. (goto-char (point-min))
  1486. (if (re-search-forward "^\\([ \t]+\\)<" 500 'noerror)
  1487. (progn
  1488. (setq-local sgml-basic-offset (1- (current-column)))
  1489. (message "Guessed sgml-basic-offset = %d"
  1490. sgml-basic-offset)
  1491. ))))
  1492. (defun sgml-parse-dtd ()
  1493. "Simplistic parse of the current buffer as a DTD.
  1494. Currently just returns (EMPTY-TAGS UNCLOSED-TAGS)."
  1495. (goto-char (point-min))
  1496. (let ((empty nil)
  1497. (unclosed nil))
  1498. (while (re-search-forward "<!ELEMENT[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+[-O][ \t\n]+\\([-O]\\)[ \t\n]+\\([^ \t\n]+\\)" nil t)
  1499. (cond
  1500. ((string= (match-string 3) "EMPTY")
  1501. (push (match-string-no-properties 1) empty))
  1502. ((string= (match-string 2) "O")
  1503. (push (match-string-no-properties 1) unclosed))))
  1504. (setq empty (sort (mapcar 'downcase empty) 'string<))
  1505. (setq unclosed (sort (mapcar 'downcase unclosed) 'string<))
  1506. (list empty unclosed)))
  1507. ;;; HTML mode
  1508. (defcustom html-mode-hook nil
  1509. "Hook run by command `html-mode'.
  1510. `text-mode-hook' and `sgml-mode-hook' are run first."
  1511. :group 'sgml
  1512. :type 'hook
  1513. :options '(html-autoview-mode))
  1514. (defvar html-quick-keys sgml-quick-keys
  1515. "Use C-c X combinations for quick insertion of frequent tags when non-nil.
  1516. This defaults to `sgml-quick-keys'.
  1517. This takes effect when first loading the library.")
  1518. (defvar html-mode-map
  1519. (let ((map (make-sparse-keymap))
  1520. (menu-map (make-sparse-keymap "HTML")))
  1521. (set-keymap-parent map sgml-mode-map)
  1522. (define-key map "\C-c6" 'html-headline-6)
  1523. (define-key map "\C-c5" 'html-headline-5)
  1524. (define-key map "\C-c4" 'html-headline-4)
  1525. (define-key map "\C-c3" 'html-headline-3)
  1526. (define-key map "\C-c2" 'html-headline-2)
  1527. (define-key map "\C-c1" 'html-headline-1)
  1528. (define-key map "\C-c\r" 'html-paragraph)
  1529. (define-key map "\C-c\n" 'html-line)
  1530. (define-key map "\C-c\C-c-" 'html-horizontal-rule)
  1531. (define-key map "\C-c\C-co" 'html-ordered-list)
  1532. (define-key map "\C-c\C-cu" 'html-unordered-list)
  1533. (define-key map "\C-c\C-cr" 'html-radio-buttons)
  1534. (define-key map "\C-c\C-cc" 'html-checkboxes)
  1535. (define-key map "\C-c\C-cl" 'html-list-item)
  1536. (define-key map "\C-c\C-ch" 'html-href-anchor)
  1537. (define-key map "\C-c\C-cn" 'html-name-anchor)
  1538. (define-key map "\C-c\C-ci" 'html-image)
  1539. (when html-quick-keys
  1540. (define-key map "\C-c-" 'html-horizontal-rule)
  1541. (define-key map "\C-co" 'html-ordered-list)
  1542. (define-key map "\C-cu" 'html-unordered-list)
  1543. (define-key map "\C-cr" 'html-radio-buttons)
  1544. (define-key map "\C-cc" 'html-checkboxes)
  1545. (define-key map "\C-cl" 'html-list-item)
  1546. (define-key map "\C-ch" 'html-href-anchor)
  1547. (define-key map "\C-cn" 'html-name-anchor)
  1548. (define-key map "\C-ci" 'html-image))
  1549. (define-key map "\C-c\C-s" 'html-autoview-mode)
  1550. (define-key map "\C-c\C-v" 'browse-url-of-buffer)
  1551. (define-key map [menu-bar html] (cons "HTML" menu-map))
  1552. (define-key menu-map [html-autoview-mode]
  1553. '("Toggle Autoviewing" . html-autoview-mode))
  1554. (define-key menu-map [browse-url-of-buffer]
  1555. '("View Buffer Contents" . browse-url-of-buffer))
  1556. (define-key menu-map [nil] '("--"))
  1557. ;;(define-key menu-map "6" '("Heading 6" . html-headline-6))
  1558. ;;(define-key menu-map "5" '("Heading 5" . html-headline-5))
  1559. ;;(define-key menu-map "4" '("Heading 4" . html-headline-4))
  1560. (define-key menu-map "3" '("Heading 3" . html-headline-3))
  1561. (define-key menu-map "2" '("Heading 2" . html-headline-2))
  1562. (define-key menu-map "1" '("Heading 1" . html-headline-1))
  1563. (define-key menu-map "l" '("Radio Buttons" . html-radio-buttons))
  1564. (define-key menu-map "c" '("Checkboxes" . html-checkboxes))
  1565. (define-key menu-map "l" '("List Item" . html-list-item))
  1566. (define-key menu-map "u" '("Unordered List" . html-unordered-list))
  1567. (define-key menu-map "o" '("Ordered List" . html-ordered-list))
  1568. (define-key menu-map "-" '("Horizontal Rule" . html-horizontal-rule))
  1569. (define-key menu-map "\n" '("Line Break" . html-line))
  1570. (define-key menu-map "\r" '("Paragraph" . html-paragraph))
  1571. (define-key menu-map "i" '("Image" . html-image))
  1572. (define-key menu-map "h" '("Href Anchor" . html-href-anchor))
  1573. (define-key menu-map "n" '("Name Anchor" . html-name-anchor))
  1574. map)
  1575. "Keymap for commands for use in HTML mode.")
  1576. (defvar html-face-tag-alist
  1577. '((bold . "b")
  1578. (italic . "i")
  1579. (underline . "u")
  1580. (mode-line . "rev"))
  1581. "Value of `sgml-face-tag-alist' for HTML mode.")
  1582. (defvar html-tag-face-alist
  1583. '(("b" . bold)
  1584. ("big" . bold)
  1585. ("blink" . highlight)
  1586. ("cite" . italic)
  1587. ("em" . italic)
  1588. ("h1" bold underline)
  1589. ("h2" bold-italic underline)
  1590. ("h3" italic underline)
  1591. ("h4" . underline)
  1592. ("h5" . underline)
  1593. ("h6" . underline)
  1594. ("i" . italic)
  1595. ("rev" . mode-line)
  1596. ("s" . underline)
  1597. ("small" . default)
  1598. ("strong" . bold)
  1599. ("title" bold underline)
  1600. ("tt" . default)
  1601. ("u" . underline)
  1602. ("var" . italic))
  1603. "Value of `sgml-tag-face-alist' for HTML mode.")
  1604. (defvar html-display-text
  1605. '((img . "[/]")
  1606. (hr . "----------")
  1607. (li . "o "))
  1608. "Value of `sgml-display-text' for HTML mode.")
  1609. ;; should code exactly HTML 3 here when that is finished
  1610. (defvar html-tag-alist
  1611. (let* ((1-7 '(("1") ("2") ("3") ("4") ("5") ("6") ("7")))
  1612. (1-9 `(,@1-7 ("8") ("9")))
  1613. (align '(("align" ("left") ("center") ("right"))))
  1614. (valign '(("top") ("middle") ("bottom") ("baseline")))
  1615. (rel '(("next") ("previous") ("parent") ("subdocument") ("made")))
  1616. (href '("href" ("ftp:") ("file:") ("finger:") ("gopher:") ("http:")
  1617. ("mailto:") ("news:") ("rlogin:") ("telnet:") ("tn3270:")
  1618. ("wais:") ("/cgi-bin/")))
  1619. (name '("name"))
  1620. (link `(,href
  1621. ("rel" ,@rel)
  1622. ("rev" ,@rel)
  1623. ("title")))
  1624. (list '((nil \n ("List item: " "<li>" str
  1625. (if sgml-xml-mode "</li>") \n))))
  1626. (cell `(t
  1627. ,@align
  1628. ("valign" ,@valign)
  1629. ("colspan" ,@1-9)
  1630. ("rowspan" ,@1-9)
  1631. ("nowrap" t))))
  1632. ;; put ,-expressions first, else byte-compile chokes (as of V19.29)
  1633. ;; and like this it's more efficient anyway
  1634. `(("a" ,name ,@link)
  1635. ("base" t ,@href)
  1636. ("dir" ,@list)
  1637. ("font" nil "size" ("-1") ("+1") ("-2") ("+2") ,@1-7)
  1638. ("form" (\n _ \n "<input type=\"submit\" value=\"\""
  1639. (if sgml-xml-mode " />" ">"))
  1640. ("action" ,@(cdr href)) ("method" ("get") ("post")))
  1641. ("h1" ,@align)
  1642. ("h2" ,@align)
  1643. ("h3" ,@align)
  1644. ("h4" ,@align)
  1645. ("h5" ,@align)
  1646. ("h6" ,@align)
  1647. ("hr" t ("size" ,@1-9) ("width") ("noshade" t) ,@align)
  1648. ("img" t ("align" ,@valign ("texttop") ("absmiddle") ("absbottom"))
  1649. ("src") ("alt") ("width" "1") ("height" "1")
  1650. ("border" "1") ("vspace" "1") ("hspace" "1") ("ismap" t))
  1651. ("input" t ("size" ,@1-9) ("maxlength" ,@1-9) ("checked" t) ,name
  1652. ("type" ("text") ("password") ("checkbox") ("radio")
  1653. ("submit") ("reset"))
  1654. ("value"))
  1655. ("link" t ,@link)
  1656. ("menu" ,@list)
  1657. ("ol" ,@list ("type" ("A") ("a") ("I") ("i") ("1")))
  1658. ("p" t ,@align)
  1659. ("select" (nil \n
  1660. ("Text: "
  1661. "<option>" str (if sgml-xml-mode "</option>") \n))
  1662. ,name ("size" ,@1-9) ("multiple" t))
  1663. ("table" (nil \n
  1664. ((completing-read "Cell kind: " '(("td") ("th"))
  1665. nil t "t")
  1666. "<tr><" str ?> _
  1667. (if sgml-xml-mode (concat "<" str "></tr>")) \n))
  1668. ("border" t ,@1-9) ("width" "10") ("cellpadding"))
  1669. ("td" ,@cell)
  1670. ("textarea" ,name ("rows" ,@1-9) ("cols" ,@1-9))
  1671. ("th" ,@cell)
  1672. ("ul" ,@list ("type" ("disc") ("circle") ("square")))
  1673. ,@sgml-tag-alist
  1674. ("abbrev")
  1675. ("acronym")
  1676. ("address")
  1677. ("array" (nil \n
  1678. ("Item: " "<item>" str (if sgml-xml-mode "</item>") \n))
  1679. "align")
  1680. ("article" \n)
  1681. ("aside" \n)
  1682. ("au")
  1683. ("b")
  1684. ("big")
  1685. ("blink")
  1686. ("blockquote" \n)
  1687. ("body" \n ("background" ".gif") ("bgcolor" "#") ("text" "#")
  1688. ("link" "#") ("alink" "#") ("vlink" "#"))
  1689. ("box" (nil _ "<over>" _ (if sgml-xml-mode "</over>")))
  1690. ("br" t ("clear" ("left") ("right")))
  1691. ("caption" ("valign" ("top") ("bottom")))
  1692. ("center" \n)
  1693. ("cite")
  1694. ("code" \n)
  1695. ("dd" ,(not sgml-xml-mode))
  1696. ("del")
  1697. ("dfn")
  1698. ("div")
  1699. ("dl" (nil \n
  1700. ( "Term: "
  1701. "<dt>" str (if sgml-xml-mode "</dt>")
  1702. "<dd>" _ (if sgml-xml-mode "</dd>") \n)))
  1703. ("dt" (t _ (if sgml-xml-mode "</dt>")
  1704. "<dd>" (if sgml-xml-mode "</dd>") \n))
  1705. ("em")
  1706. ("fn" "id" "fn") ;; Footnotes were deprecated in HTML 3.2
  1707. ("footer" \n)
  1708. ("head" \n)
  1709. ("header" \n)
  1710. ("hgroup" \n)
  1711. ("html" (\n
  1712. "<head>\n"
  1713. "<title>" (setq str (read-input "Title: ")) "</title>\n"
  1714. "</head>\n"
  1715. "<body>\n<h1>" str "</h1>\n" _
  1716. "\n<address>\n<a href=\"mailto:"
  1717. user-mail-address
  1718. "\">" (user-full-name) "</a>\n</address>\n"
  1719. "</body>"
  1720. ))
  1721. ("i")
  1722. ("ins")
  1723. ("isindex" t ("action") ("prompt"))
  1724. ("kbd")
  1725. ("lang")
  1726. ("li" ,(not sgml-xml-mode))
  1727. ("math" \n)
  1728. ("nav" \n)
  1729. ("nobr")
  1730. ("option" t ("value") ("label") ("selected" t))
  1731. ("over" t)
  1732. ("person") ;; Tag for person's name tag deprecated in HTML 3.2
  1733. ("pre" \n)
  1734. ("q")
  1735. ("rev")
  1736. ("s")
  1737. ("samp")
  1738. ("section" \n)
  1739. ("small")
  1740. ("span" nil
  1741. ("class"
  1742. ("builtin")
  1743. ("comment")
  1744. ("constant")
  1745. ("function-name")
  1746. ("keyword")
  1747. ("string")
  1748. ("type")
  1749. ("variable-name")
  1750. ("warning")))
  1751. ("strong")
  1752. ("sub")
  1753. ("sup")
  1754. ("title")
  1755. ("tr" t)
  1756. ("tt")
  1757. ("u")
  1758. ("var")
  1759. ("wbr" t)))
  1760. "Value of `sgml-tag-alist' for HTML mode.")
  1761. (defvar html-tag-help
  1762. `(,@sgml-tag-help
  1763. ("a" . "Anchor of point or link elsewhere")
  1764. ("abbrev" . "Abbreviation")
  1765. ("acronym" . "Acronym")
  1766. ("address" . "Formatted mail address")
  1767. ("array" . "Math array")
  1768. ("article" . "An independent part of document or site")
  1769. ("aside" . "Secondary content related to surrounding content (e.g. page or article)")
  1770. ("au" . "Author")
  1771. ("b" . "Bold face")
  1772. ("base" . "Base address for URLs")
  1773. ("big" . "Font size")
  1774. ("blink" . "Blinking text")
  1775. ("blockquote" . "Indented quotation")
  1776. ("body" . "Document body")
  1777. ("box" . "Math fraction")
  1778. ("br" . "Line break")
  1779. ("caption" . "Table caption")
  1780. ("center" . "Centered text")
  1781. ("changed" . "Change bars")
  1782. ("cite" . "Citation of a document")
  1783. ("code" . "Formatted source code")
  1784. ("dd" . "Definition of term")
  1785. ("del" . "Deleted text")
  1786. ("dfn" . "Defining instance of a term")
  1787. ("dir" . "Directory list (obsolete)")
  1788. ("div" . "Generic block-level container")
  1789. ("dl" . "Definition list")
  1790. ("dt" . "Term to be defined")
  1791. ("em" . "Emphasized")
  1792. ("embed" . "Embedded data in foreign format")
  1793. ("fig" . "Figure")
  1794. ("figa" . "Figure anchor")
  1795. ("figd" . "Figure description")
  1796. ("figt" . "Figure text")
  1797. ("fn" . "Footnote") ;; No one supports special footnote rendering.
  1798. ("font" . "Font size")
  1799. ("footer" . "Footer of a section")
  1800. ("form" . "Form with input fields")
  1801. ("group" . "Document grouping")
  1802. ("h1" . "Most important section headline")
  1803. ("h2" . "Important section headline")
  1804. ("h3" . "Section headline")
  1805. ("h4" . "Minor section headline")
  1806. ("h5" . "Unimportant section headline")
  1807. ("h6" . "Least important section headline")
  1808. ("head" . "Document header")
  1809. ("header" . "Header of a section")
  1810. ("hgroup" . "Group of headings - h1-h6 elements")
  1811. ("hr" . "Horizontal rule")
  1812. ("html" . "HTML Document")
  1813. ("i" . "Italic face")
  1814. ("img" . "Graphic image")
  1815. ("input" . "Form input field")
  1816. ("ins" . "Inserted text")
  1817. ("isindex" . "Input field for index search")
  1818. ("kbd" . "Keyboard example face")
  1819. ("lang" . "Natural language")
  1820. ("li" . "List item")
  1821. ("link" . "Link relationship")
  1822. ("math" . "Math formula")
  1823. ("menu" . "List of commands")
  1824. ("mh" . "Form mail header")
  1825. ("nav" . "Group of navigational links")
  1826. ("nextid" . "Allocate new id")
  1827. ("nobr" . "Text without line break")
  1828. ("ol" . "Ordered list")
  1829. ("option" . "Selection list item")
  1830. ("over" . "Math fraction rule")
  1831. ("p" . "Paragraph start")
  1832. ("panel" . "Floating panel")
  1833. ("person" . "Person's name")
  1834. ("pre" . "Preformatted fixed width text")
  1835. ("q" . "Quotation")
  1836. ("rev" . "Reverse video")
  1837. ("s" . "Strikeout")
  1838. ("samp" . "Sample text")
  1839. ("section" . "Section of a document")
  1840. ("select" . "Selection list")
  1841. ("small" . "Font size")
  1842. ("sp" . "Nobreak space")
  1843. ("span" . "Generic inline container")
  1844. ("strong" . "Standout text")
  1845. ("sub" . "Subscript")
  1846. ("sup" . "Superscript")
  1847. ("table" . "Table with rows and columns")
  1848. ("tb" . "Table vertical break")
  1849. ("td" . "Table data cell")
  1850. ("textarea" . "Form multiline edit area")
  1851. ("th" . "Table header cell")
  1852. ("title" . "Document title")
  1853. ("tr" . "Table row separator")
  1854. ("tt" . "Typewriter face")
  1855. ("u" . "Underlined text")
  1856. ("ul" . "Unordered list")
  1857. ("var" . "Math variable face")
  1858. ("wbr" . "Enable <br> within <nobr>"))
  1859. "Value of variable `sgml-tag-help' for HTML mode.")
  1860. (defvar outline-regexp)
  1861. (defvar outline-heading-end-regexp)
  1862. (defvar outline-level)
  1863. (defun html-current-defun-name ()
  1864. "Return the name of the last HTML title or heading, or nil."
  1865. (save-excursion
  1866. (if (re-search-backward
  1867. (concat
  1868. "<[ \t\r\n]*"
  1869. "\\(?:[hH][0-6]\\|title\\|TITLE\\|Title\\)"
  1870. "[^>]*>"
  1871. "[ \t\r\n]*"
  1872. "\\([^<\r\n]*[^ <\t\r\n]+\\)")
  1873. nil t)
  1874. (match-string-no-properties 1))))
  1875. ;;;###autoload
  1876. (define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML")
  1877. "Major mode based on SGML mode for editing HTML documents.
  1878. This allows inserting skeleton constructs used in hypertext documents with
  1879. completion. See below for an introduction to HTML. Use
  1880. \\[browse-url-of-buffer] to see how this comes out. See also `sgml-mode' on
  1881. which this is based.
  1882. Do \\[describe-variable] html- SPC and \\[describe-variable] sgml- SPC to see available variables.
  1883. To write fairly well formatted pages you only need to know few things. Most
  1884. browsers have a function to read the source code of the page being seen, so
  1885. you can imitate various tricks. Here's a very short HTML primer which you
  1886. can also view with a browser to see what happens:
  1887. <title>A Title Describing Contents</title> should be on every page. Pages can
  1888. have <h1>Very Major Headlines</h1> through <h6>Very Minor Headlines</h6>
  1889. <hr> Parts can be separated with horizontal rules.
  1890. <p>Paragraphs only need an opening tag. Line breaks and multiple spaces are
  1891. ignored unless the text is <pre>preformatted.</pre> Text can be marked as
  1892. <b>bold</b>, <i>italic</i> or <u>underlined</u> using the normal M-o or
  1893. Edit/Text Properties/Face commands.
  1894. Pages can have <a name=\"SOMENAME\">named points</a> and can link other points
  1895. to them with <a href=\"#SOMENAME\">see also somename</a>. In the same way <a
  1896. href=\"URL\">see also URL</a> where URL is a filename relative to current
  1897. directory, or absolute as in `http://www.cs.indiana.edu/elisp/w3/docs.html'.
  1898. Images in many formats can be inlined with <img src=\"URL\">.
  1899. If you mainly create your own documents, `sgml-specials' might be
  1900. interesting. But note that some HTML 2 browsers can't handle `&apos;'.
  1901. To work around that, do:
  1902. (eval-after-load \"sgml-mode\" \\='(aset sgml-char-names ?\\=' nil))
  1903. \\{html-mode-map}"
  1904. (setq-local sgml-display-text html-display-text)
  1905. (setq-local sgml-tag-face-alist html-tag-face-alist)
  1906. (setq-local sgml-tag-alist html-tag-alist)
  1907. (setq-local sgml-face-tag-alist html-face-tag-alist)
  1908. (setq-local sgml-tag-help html-tag-help)
  1909. (setq-local outline-regexp "^.*<[Hh][1-6]\\>")
  1910. (setq-local outline-heading-end-regexp "</[Hh][1-6]>")
  1911. (setq-local outline-level
  1912. (lambda () (char-before (match-end 0))))
  1913. (setq-local add-log-current-defun-function #'html-current-defun-name)
  1914. (setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*")
  1915. (setq imenu-create-index-function 'html-imenu-index)
  1916. (setq-local sgml-empty-tags
  1917. ;; From HTML-4.01's loose.dtd, parsed with
  1918. ;; `sgml-parse-dtd', plus manual addition of "wbr".
  1919. '("area" "base" "basefont" "br" "col" "frame" "hr" "img" "input"
  1920. "isindex" "link" "meta" "param" "wbr"))
  1921. (setq-local sgml-unclosed-tags
  1922. ;; From HTML-4.01's loose.dtd, parsed with `sgml-parse-dtd'.
  1923. '("body" "colgroup" "dd" "dt" "head" "html" "li" "option"
  1924. "p" "tbody" "td" "tfoot" "th" "thead" "tr"))
  1925. ;; It's for the user to decide if it defeats it or not -stef
  1926. ;; (make-local-variable 'imenu-sort-function)
  1927. ;; (setq imenu-sort-function nil) ; sorting the menu defeats the purpose
  1928. )
  1929. (defvar html-imenu-regexp
  1930. "\\s-*<h\\([1-9]\\)[^\n<>]*>\\(<[^\n<>]*>\\)*\\s-*\\([^\n<>]*\\)"
  1931. "A regular expression matching a head line to be added to the menu.
  1932. The first `match-string' should be a number from 1-9.
  1933. The second `match-string' matches extra tags and is ignored.
  1934. The third `match-string' will be the used in the menu.")
  1935. (defun html-imenu-index ()
  1936. "Return a table of contents for an HTML buffer for use with Imenu."
  1937. (let (toc-index)
  1938. (save-excursion
  1939. (goto-char (point-min))
  1940. (while (re-search-forward html-imenu-regexp nil t)
  1941. (setq toc-index
  1942. (cons (cons (concat (make-string
  1943. (* 2 (1- (string-to-number (match-string 1))))
  1944. ?\s)
  1945. (match-string 3))
  1946. (line-beginning-position))
  1947. toc-index))))
  1948. (nreverse toc-index)))
  1949. (define-minor-mode html-autoview-mode
  1950. "Toggle viewing of HTML files on save (HTML Autoview mode).
  1951. With a prefix argument ARG, enable HTML Autoview mode if ARG is
  1952. positive, and disable it otherwise. If called from Lisp, enable
  1953. the mode if ARG is omitted or nil.
  1954. HTML Autoview mode is a buffer-local minor mode for use with
  1955. `html-mode'. If enabled, saving the file automatically runs
  1956. `browse-url-of-buffer' to view it."
  1957. nil nil nil
  1958. :group 'sgml
  1959. (if html-autoview-mode
  1960. (add-hook 'after-save-hook 'browse-url-of-buffer nil t)
  1961. (remove-hook 'after-save-hook 'browse-url-of-buffer t)))
  1962. (define-skeleton html-href-anchor
  1963. "HTML anchor tag with href attribute."
  1964. "URL: "
  1965. ;; '(setq input "http:")
  1966. "<a href=\"" str "\">" _ "</a>")
  1967. (define-skeleton html-name-anchor
  1968. "HTML anchor tag with name attribute."
  1969. "Name: "
  1970. "<a name=\"" str "\""
  1971. (if sgml-xml-mode (concat " id=\"" str "\""))
  1972. ">" _ "</a>")
  1973. (define-skeleton html-headline-1
  1974. "HTML level 1 headline tags."
  1975. nil
  1976. "<h1>" _ "</h1>")
  1977. (define-skeleton html-headline-2
  1978. "HTML level 2 headline tags."
  1979. nil
  1980. "<h2>" _ "</h2>")
  1981. (define-skeleton html-headline-3
  1982. "HTML level 3 headline tags."
  1983. nil
  1984. "<h3>" _ "</h3>")
  1985. (define-skeleton html-headline-4
  1986. "HTML level 4 headline tags."
  1987. nil
  1988. "<h4>" _ "</h4>")
  1989. (define-skeleton html-headline-5
  1990. "HTML level 5 headline tags."
  1991. nil
  1992. "<h5>" _ "</h5>")
  1993. (define-skeleton html-headline-6
  1994. "HTML level 6 headline tags."
  1995. nil
  1996. "<h6>" _ "</h6>")
  1997. (define-skeleton html-horizontal-rule
  1998. "HTML horizontal rule tag."
  1999. nil
  2000. (if sgml-xml-mode "<hr />" "<hr>") \n)
  2001. (define-skeleton html-image
  2002. "HTML image tag."
  2003. "Image URL: "
  2004. "<img src=\"" str "\" alt=\"" _ "\""
  2005. (if sgml-xml-mode " />" ">"))
  2006. (define-skeleton html-line
  2007. "HTML line break tag."
  2008. nil
  2009. (if sgml-xml-mode "<br />" "<br>") \n)
  2010. (define-skeleton html-ordered-list
  2011. "HTML ordered list tags."
  2012. nil
  2013. "<ol>" \n
  2014. "<li>" _ (if sgml-xml-mode "</li>") \n
  2015. "</ol>")
  2016. (define-skeleton html-unordered-list
  2017. "HTML unordered list tags."
  2018. nil
  2019. "<ul>" \n
  2020. "<li>" _ (if sgml-xml-mode "</li>") \n
  2021. "</ul>")
  2022. (define-skeleton html-list-item
  2023. "HTML list item tag."
  2024. nil
  2025. (if (bolp) nil '\n)
  2026. "<li>" _ (if sgml-xml-mode "</li>"))
  2027. (define-skeleton html-paragraph
  2028. "HTML paragraph tag."
  2029. nil
  2030. (if (bolp) nil ?\n)
  2031. "<p>" _ (if sgml-xml-mode "</p>"))
  2032. (define-skeleton html-checkboxes
  2033. "Group of connected checkbox inputs."
  2034. nil
  2035. '(setq v1 nil
  2036. v2 nil)
  2037. ("Value: "
  2038. "<input type=\"" (identity "checkbox") ; see comment above about identity
  2039. "\" name=\"" (or v1 (setq v1 (skeleton-read "Name: ")))
  2040. "\" value=\"" str ?\"
  2041. (when (y-or-n-p "Set \"checked\" attribute? ")
  2042. (funcall skeleton-transformation-function
  2043. (if sgml-xml-mode " checked=\"checked\"" " checked")))
  2044. (if sgml-xml-mode " />" ">")
  2045. (skeleton-read "Text: " (capitalize str))
  2046. (or v2 (setq v2 (if (y-or-n-p "Newline after text? ")
  2047. (funcall skeleton-transformation-function
  2048. (if sgml-xml-mode "<br />" "<br>"))
  2049. "")))
  2050. \n))
  2051. (define-skeleton html-radio-buttons
  2052. "Group of connected radio button inputs."
  2053. nil
  2054. '(setq v1 nil
  2055. v2 (cons nil nil))
  2056. ("Value: "
  2057. "<input type=\"" (identity "radio") ; see comment above about identity
  2058. "\" name=\"" (or (car v2) (setcar v2 (skeleton-read "Name: ")))
  2059. "\" value=\"" str ?\"
  2060. (when (and (not v1) (setq v1 (y-or-n-p "Set \"checked\" attribute? ")))
  2061. (funcall skeleton-transformation-function
  2062. (if sgml-xml-mode " checked=\"checked\"" " checked")))
  2063. (if sgml-xml-mode " />" ">")
  2064. (skeleton-read "Text: " (capitalize str))
  2065. (or (cdr v2) (setcdr v2 (if (y-or-n-p "Newline after text? ")
  2066. (funcall skeleton-transformation-function
  2067. (if sgml-xml-mode "<br />" "<br>"))
  2068. "")))
  2069. \n))
  2070. (define-skeleton html-navigational-links
  2071. "Group of navigational links."
  2072. nil
  2073. "<nav>" \n
  2074. "<ul>" \n
  2075. "<li><a href=\"" (skeleton-read "URL: " "#") "\">"
  2076. (skeleton-read "Title: ") "</a>"
  2077. (if sgml-xml-mode (if sgml-xml-mode "</li>")) \n
  2078. "</ul>" \n
  2079. "</nav>")
  2080. (define-skeleton html-html5-template
  2081. "Initial HTML5 template"
  2082. nil
  2083. "<!DOCTYPE html>" \n
  2084. "<html lang=\"en\">" \n
  2085. "<head>" \n
  2086. "<meta charset=\"utf-8\">" \n
  2087. "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" \n
  2088. "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" \n
  2089. "<title>" (skeleton-read "Page Title: ") "</title>" \n
  2090. "</head>" \n
  2091. "<body>" \n
  2092. "<div id=\"app\"></div>" \n
  2093. "</body>" \n
  2094. "</html>")
  2095. (provide 'sgml-mode)
  2096. ;;; sgml-mode.el ends here