sgml-mode.el 92 KB

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