sgml-mode.el 77 KB

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