html.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. """
  2. pygments.lexers.html
  3. ~~~~~~~~~~~~~~~~~~~~
  4. Lexers for HTML, XML and related markup.
  5. :copyright: Copyright 2006-2021 by the Pygments team, see AUTHORS.
  6. :license: BSD, see LICENSE for details.
  7. """
  8. import re
  9. from pygments.lexer import RegexLexer, ExtendedRegexLexer, include, bygroups, \
  10. default, using
  11. from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
  12. Punctuation
  13. from pygments.util import looks_like_xml, html_doctype_matches
  14. from pygments.lexers.javascript import JavascriptLexer
  15. from pygments.lexers.jvm import ScalaLexer
  16. from pygments.lexers.css import CssLexer, _indentation, _starts_block
  17. from pygments.lexers.ruby import RubyLexer
  18. __all__ = ['HtmlLexer', 'DtdLexer', 'XmlLexer', 'XsltLexer', 'HamlLexer',
  19. 'ScamlLexer', 'PugLexer']
  20. class HtmlLexer(RegexLexer):
  21. """
  22. For HTML 4 and XHTML 1 markup. Nested JavaScript and CSS is highlighted
  23. by the appropriate lexer.
  24. """
  25. name = 'HTML'
  26. aliases = ['html']
  27. filenames = ['*.html', '*.htm', '*.xhtml', '*.xslt']
  28. mimetypes = ['text/html', 'application/xhtml+xml']
  29. flags = re.IGNORECASE | re.DOTALL
  30. tokens = {
  31. 'root': [
  32. ('[^<&]+', Text),
  33. (r'&\S*?;', Name.Entity),
  34. (r'\<\!\[CDATA\[.*?\]\]\>', Comment.Preproc),
  35. ('<!--', Comment, 'comment'),
  36. (r'<\?.*?\?>', Comment.Preproc),
  37. ('<![^>]*>', Comment.Preproc),
  38. (r'(<)(\s*)(script)(\s*)',
  39. bygroups(Punctuation, Text, Name.Tag, Text),
  40. ('script-content', 'tag')),
  41. (r'(<)(\s*)(style)(\s*)',
  42. bygroups(Punctuation, Text, Name.Tag, Text),
  43. ('style-content', 'tag')),
  44. # note: this allows tag names not used in HTML like <x:with-dash>,
  45. # this is to support yet-unknown template engines and the like
  46. (r'(<)(\s*)([\w:.-]+)',
  47. bygroups(Punctuation, Text, Name.Tag), 'tag'),
  48. (r'(<)(\s*)(/)(\s*)([\w:.-]+)(\s*)(>)',
  49. bygroups(Punctuation, Text, Punctuation, Text, Name.Tag, Text,
  50. Punctuation)),
  51. ],
  52. 'comment': [
  53. ('[^-]+', Comment),
  54. ('-->', Comment, '#pop'),
  55. ('-', Comment),
  56. ],
  57. 'tag': [
  58. (r'\s+', Text),
  59. (r'([\w:-]+\s*)(=)(\s*)', bygroups(Name.Attribute, Operator, Text),
  60. 'attr'),
  61. (r'[\w:-]+', Name.Attribute),
  62. (r'(/?)(\s*)(>)', bygroups(Punctuation, Text, Punctuation), '#pop'),
  63. ],
  64. 'script-content': [
  65. (r'(<)(\s*)(/)(\s*)(script)(\s*)(>)',
  66. bygroups(Punctuation, Text, Punctuation, Text, Name.Tag, Text,
  67. Punctuation), '#pop'),
  68. (r'.+?(?=<\s*/\s*script\s*>)', using(JavascriptLexer)),
  69. # fallback cases for when there is no closing script tag
  70. # first look for newline and then go back into root state
  71. # if that fails just read the rest of the file
  72. # this is similar to the error handling logic in lexer.py
  73. (r'.+?\n', using(JavascriptLexer), '#pop'),
  74. (r'.+', using(JavascriptLexer), '#pop'),
  75. ],
  76. 'style-content': [
  77. (r'(<)(\s*)(/)(\s*)(style)(\s*)(>)',
  78. bygroups(Punctuation, Text, Punctuation, Text, Name.Tag, Text,
  79. Punctuation),'#pop'),
  80. (r'.+?(?=<\s*/\s*style\s*>)', using(CssLexer)),
  81. # fallback cases for when there is no closing style tag
  82. # first look for newline and then go back into root state
  83. # if that fails just read the rest of the file
  84. # this is similar to the error handling logic in lexer.py
  85. (r'.+?\n', using(CssLexer), '#pop'),
  86. (r'.+', using(CssLexer), '#pop'),
  87. ],
  88. 'attr': [
  89. ('".*?"', String, '#pop'),
  90. ("'.*?'", String, '#pop'),
  91. (r'[^\s>]+', String, '#pop'),
  92. ],
  93. }
  94. def analyse_text(text):
  95. if html_doctype_matches(text):
  96. return 0.5
  97. class DtdLexer(RegexLexer):
  98. """
  99. A lexer for DTDs (Document Type Definitions).
  100. .. versionadded:: 1.5
  101. """
  102. flags = re.MULTILINE | re.DOTALL
  103. name = 'DTD'
  104. aliases = ['dtd']
  105. filenames = ['*.dtd']
  106. mimetypes = ['application/xml-dtd']
  107. tokens = {
  108. 'root': [
  109. include('common'),
  110. (r'(<!ELEMENT)(\s+)(\S+)',
  111. bygroups(Keyword, Text, Name.Tag), 'element'),
  112. (r'(<!ATTLIST)(\s+)(\S+)',
  113. bygroups(Keyword, Text, Name.Tag), 'attlist'),
  114. (r'(<!ENTITY)(\s+)(\S+)',
  115. bygroups(Keyword, Text, Name.Entity), 'entity'),
  116. (r'(<!NOTATION)(\s+)(\S+)',
  117. bygroups(Keyword, Text, Name.Tag), 'notation'),
  118. (r'(<!\[)([^\[\s]+)(\s*)(\[)', # conditional sections
  119. bygroups(Keyword, Name.Entity, Text, Keyword)),
  120. (r'(<!DOCTYPE)(\s+)([^>\s]+)',
  121. bygroups(Keyword, Text, Name.Tag)),
  122. (r'PUBLIC|SYSTEM', Keyword.Constant),
  123. (r'[\[\]>]', Keyword),
  124. ],
  125. 'common': [
  126. (r'\s+', Text),
  127. (r'(%|&)[^;]*;', Name.Entity),
  128. ('<!--', Comment, 'comment'),
  129. (r'[(|)*,?+]', Operator),
  130. (r'"[^"]*"', String.Double),
  131. (r'\'[^\']*\'', String.Single),
  132. ],
  133. 'comment': [
  134. ('[^-]+', Comment),
  135. ('-->', Comment, '#pop'),
  136. ('-', Comment),
  137. ],
  138. 'element': [
  139. include('common'),
  140. (r'EMPTY|ANY|#PCDATA', Keyword.Constant),
  141. (r'[^>\s|()?+*,]+', Name.Tag),
  142. (r'>', Keyword, '#pop'),
  143. ],
  144. 'attlist': [
  145. include('common'),
  146. (r'CDATA|IDREFS|IDREF|ID|NMTOKENS|NMTOKEN|ENTITIES|ENTITY|NOTATION',
  147. Keyword.Constant),
  148. (r'#REQUIRED|#IMPLIED|#FIXED', Keyword.Constant),
  149. (r'xml:space|xml:lang', Keyword.Reserved),
  150. (r'[^>\s|()?+*,]+', Name.Attribute),
  151. (r'>', Keyword, '#pop'),
  152. ],
  153. 'entity': [
  154. include('common'),
  155. (r'SYSTEM|PUBLIC|NDATA', Keyword.Constant),
  156. (r'[^>\s|()?+*,]+', Name.Entity),
  157. (r'>', Keyword, '#pop'),
  158. ],
  159. 'notation': [
  160. include('common'),
  161. (r'SYSTEM|PUBLIC', Keyword.Constant),
  162. (r'[^>\s|()?+*,]+', Name.Attribute),
  163. (r'>', Keyword, '#pop'),
  164. ],
  165. }
  166. def analyse_text(text):
  167. if not looks_like_xml(text) and \
  168. ('<!ELEMENT' in text or '<!ATTLIST' in text or '<!ENTITY' in text):
  169. return 0.8
  170. class XmlLexer(RegexLexer):
  171. """
  172. Generic lexer for XML (eXtensible Markup Language).
  173. """
  174. flags = re.MULTILINE | re.DOTALL | re.UNICODE
  175. name = 'XML'
  176. aliases = ['xml']
  177. filenames = ['*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd',
  178. '*.wsdl', '*.wsf']
  179. mimetypes = ['text/xml', 'application/xml', 'image/svg+xml',
  180. 'application/rss+xml', 'application/atom+xml']
  181. tokens = {
  182. 'root': [
  183. ('[^<&]+', Text),
  184. (r'&\S*?;', Name.Entity),
  185. (r'\<\!\[CDATA\[.*?\]\]\>', Comment.Preproc),
  186. ('<!--', Comment, 'comment'),
  187. (r'<\?.*?\?>', Comment.Preproc),
  188. ('<![^>]*>', Comment.Preproc),
  189. (r'<\s*[\w:.-]+', Name.Tag, 'tag'),
  190. (r'<\s*/\s*[\w:.-]+\s*>', Name.Tag),
  191. ],
  192. 'comment': [
  193. ('[^-]+', Comment),
  194. ('-->', Comment, '#pop'),
  195. ('-', Comment),
  196. ],
  197. 'tag': [
  198. (r'\s+', Text),
  199. (r'[\w.:-]+\s*=', Name.Attribute, 'attr'),
  200. (r'/?\s*>', Name.Tag, '#pop'),
  201. ],
  202. 'attr': [
  203. (r'\s+', Text),
  204. ('".*?"', String, '#pop'),
  205. ("'.*?'", String, '#pop'),
  206. (r'[^\s>]+', String, '#pop'),
  207. ],
  208. }
  209. def analyse_text(text):
  210. if looks_like_xml(text):
  211. return 0.45 # less than HTML
  212. class XsltLexer(XmlLexer):
  213. """
  214. A lexer for XSLT.
  215. .. versionadded:: 0.10
  216. """
  217. name = 'XSLT'
  218. aliases = ['xslt']
  219. filenames = ['*.xsl', '*.xslt', '*.xpl'] # xpl is XProc
  220. mimetypes = ['application/xsl+xml', 'application/xslt+xml']
  221. EXTRA_KEYWORDS = {
  222. 'apply-imports', 'apply-templates', 'attribute',
  223. 'attribute-set', 'call-template', 'choose', 'comment',
  224. 'copy', 'copy-of', 'decimal-format', 'element', 'fallback',
  225. 'for-each', 'if', 'import', 'include', 'key', 'message',
  226. 'namespace-alias', 'number', 'otherwise', 'output', 'param',
  227. 'preserve-space', 'processing-instruction', 'sort',
  228. 'strip-space', 'stylesheet', 'template', 'text', 'transform',
  229. 'value-of', 'variable', 'when', 'with-param'
  230. }
  231. def get_tokens_unprocessed(self, text):
  232. for index, token, value in XmlLexer.get_tokens_unprocessed(self, text):
  233. m = re.match('</?xsl:([^>]*)/?>?', value)
  234. if token is Name.Tag and m and m.group(1) in self.EXTRA_KEYWORDS:
  235. yield index, Keyword, value
  236. else:
  237. yield index, token, value
  238. def analyse_text(text):
  239. if looks_like_xml(text) and '<xsl' in text:
  240. return 0.8
  241. class HamlLexer(ExtendedRegexLexer):
  242. """
  243. For Haml markup.
  244. .. versionadded:: 1.3
  245. """
  246. name = 'Haml'
  247. aliases = ['haml']
  248. filenames = ['*.haml']
  249. mimetypes = ['text/x-haml']
  250. flags = re.IGNORECASE
  251. # Haml can include " |\n" anywhere,
  252. # which is ignored and used to wrap long lines.
  253. # To accomodate this, use this custom faux dot instead.
  254. _dot = r'(?: \|\n(?=.* \|)|.)'
  255. # In certain places, a comma at the end of the line
  256. # allows line wrapping as well.
  257. _comma_dot = r'(?:,\s*\n|' + _dot + ')'
  258. tokens = {
  259. 'root': [
  260. (r'[ \t]*\n', Text),
  261. (r'[ \t]*', _indentation),
  262. ],
  263. 'css': [
  264. (r'\.[\w:-]+', Name.Class, 'tag'),
  265. (r'\#[\w:-]+', Name.Function, 'tag'),
  266. ],
  267. 'eval-or-plain': [
  268. (r'[&!]?==', Punctuation, 'plain'),
  269. (r'([&!]?[=~])(' + _comma_dot + r'*\n)',
  270. bygroups(Punctuation, using(RubyLexer)),
  271. 'root'),
  272. default('plain'),
  273. ],
  274. 'content': [
  275. include('css'),
  276. (r'%[\w:-]+', Name.Tag, 'tag'),
  277. (r'!!!' + _dot + r'*\n', Name.Namespace, '#pop'),
  278. (r'(/)(\[' + _dot + r'*?\])(' + _dot + r'*\n)',
  279. bygroups(Comment, Comment.Special, Comment),
  280. '#pop'),
  281. (r'/' + _dot + r'*\n', _starts_block(Comment, 'html-comment-block'),
  282. '#pop'),
  283. (r'-#' + _dot + r'*\n', _starts_block(Comment.Preproc,
  284. 'haml-comment-block'), '#pop'),
  285. (r'(-)(' + _comma_dot + r'*\n)',
  286. bygroups(Punctuation, using(RubyLexer)),
  287. '#pop'),
  288. (r':' + _dot + r'*\n', _starts_block(Name.Decorator, 'filter-block'),
  289. '#pop'),
  290. include('eval-or-plain'),
  291. ],
  292. 'tag': [
  293. include('css'),
  294. (r'\{(,\n|' + _dot + r')*?\}', using(RubyLexer)),
  295. (r'\[' + _dot + r'*?\]', using(RubyLexer)),
  296. (r'\(', Text, 'html-attributes'),
  297. (r'/[ \t]*\n', Punctuation, '#pop:2'),
  298. (r'[<>]{1,2}(?=[ \t=])', Punctuation),
  299. include('eval-or-plain'),
  300. ],
  301. 'plain': [
  302. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Text),
  303. (r'(#\{)(' + _dot + r'*?)(\})',
  304. bygroups(String.Interpol, using(RubyLexer), String.Interpol)),
  305. (r'\n', Text, 'root'),
  306. ],
  307. 'html-attributes': [
  308. (r'\s+', Text),
  309. (r'[\w:-]+[ \t]*=', Name.Attribute, 'html-attribute-value'),
  310. (r'[\w:-]+', Name.Attribute),
  311. (r'\)', Text, '#pop'),
  312. ],
  313. 'html-attribute-value': [
  314. (r'[ \t]+', Text),
  315. (r'\w+', Name.Variable, '#pop'),
  316. (r'@\w+', Name.Variable.Instance, '#pop'),
  317. (r'\$\w+', Name.Variable.Global, '#pop'),
  318. (r"'(\\\\|\\[^\\]|[^'\\\n])*'", String, '#pop'),
  319. (r'"(\\\\|\\[^\\]|[^"\\\n])*"', String, '#pop'),
  320. ],
  321. 'html-comment-block': [
  322. (_dot + '+', Comment),
  323. (r'\n', Text, 'root'),
  324. ],
  325. 'haml-comment-block': [
  326. (_dot + '+', Comment.Preproc),
  327. (r'\n', Text, 'root'),
  328. ],
  329. 'filter-block': [
  330. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Name.Decorator),
  331. (r'(#\{)(' + _dot + r'*?)(\})',
  332. bygroups(String.Interpol, using(RubyLexer), String.Interpol)),
  333. (r'\n', Text, 'root'),
  334. ],
  335. }
  336. class ScamlLexer(ExtendedRegexLexer):
  337. """
  338. For `Scaml markup <http://scalate.fusesource.org/>`_. Scaml is Haml for Scala.
  339. .. versionadded:: 1.4
  340. """
  341. name = 'Scaml'
  342. aliases = ['scaml']
  343. filenames = ['*.scaml']
  344. mimetypes = ['text/x-scaml']
  345. flags = re.IGNORECASE
  346. # Scaml does not yet support the " |\n" notation to
  347. # wrap long lines. Once it does, use the custom faux
  348. # dot instead.
  349. # _dot = r'(?: \|\n(?=.* \|)|.)'
  350. _dot = r'.'
  351. tokens = {
  352. 'root': [
  353. (r'[ \t]*\n', Text),
  354. (r'[ \t]*', _indentation),
  355. ],
  356. 'css': [
  357. (r'\.[\w:-]+', Name.Class, 'tag'),
  358. (r'\#[\w:-]+', Name.Function, 'tag'),
  359. ],
  360. 'eval-or-plain': [
  361. (r'[&!]?==', Punctuation, 'plain'),
  362. (r'([&!]?[=~])(' + _dot + r'*\n)',
  363. bygroups(Punctuation, using(ScalaLexer)),
  364. 'root'),
  365. default('plain'),
  366. ],
  367. 'content': [
  368. include('css'),
  369. (r'%[\w:-]+', Name.Tag, 'tag'),
  370. (r'!!!' + _dot + r'*\n', Name.Namespace, '#pop'),
  371. (r'(/)(\[' + _dot + r'*?\])(' + _dot + r'*\n)',
  372. bygroups(Comment, Comment.Special, Comment),
  373. '#pop'),
  374. (r'/' + _dot + r'*\n', _starts_block(Comment, 'html-comment-block'),
  375. '#pop'),
  376. (r'-#' + _dot + r'*\n', _starts_block(Comment.Preproc,
  377. 'scaml-comment-block'), '#pop'),
  378. (r'(-@\s*)(import)?(' + _dot + r'*\n)',
  379. bygroups(Punctuation, Keyword, using(ScalaLexer)),
  380. '#pop'),
  381. (r'(-)(' + _dot + r'*\n)',
  382. bygroups(Punctuation, using(ScalaLexer)),
  383. '#pop'),
  384. (r':' + _dot + r'*\n', _starts_block(Name.Decorator, 'filter-block'),
  385. '#pop'),
  386. include('eval-or-plain'),
  387. ],
  388. 'tag': [
  389. include('css'),
  390. (r'\{(,\n|' + _dot + r')*?\}', using(ScalaLexer)),
  391. (r'\[' + _dot + r'*?\]', using(ScalaLexer)),
  392. (r'\(', Text, 'html-attributes'),
  393. (r'/[ \t]*\n', Punctuation, '#pop:2'),
  394. (r'[<>]{1,2}(?=[ \t=])', Punctuation),
  395. include('eval-or-plain'),
  396. ],
  397. 'plain': [
  398. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Text),
  399. (r'(#\{)(' + _dot + r'*?)(\})',
  400. bygroups(String.Interpol, using(ScalaLexer), String.Interpol)),
  401. (r'\n', Text, 'root'),
  402. ],
  403. 'html-attributes': [
  404. (r'\s+', Text),
  405. (r'[\w:-]+[ \t]*=', Name.Attribute, 'html-attribute-value'),
  406. (r'[\w:-]+', Name.Attribute),
  407. (r'\)', Text, '#pop'),
  408. ],
  409. 'html-attribute-value': [
  410. (r'[ \t]+', Text),
  411. (r'\w+', Name.Variable, '#pop'),
  412. (r'@\w+', Name.Variable.Instance, '#pop'),
  413. (r'\$\w+', Name.Variable.Global, '#pop'),
  414. (r"'(\\\\|\\[^\\]|[^'\\\n])*'", String, '#pop'),
  415. (r'"(\\\\|\\[^\\]|[^"\\\n])*"', String, '#pop'),
  416. ],
  417. 'html-comment-block': [
  418. (_dot + '+', Comment),
  419. (r'\n', Text, 'root'),
  420. ],
  421. 'scaml-comment-block': [
  422. (_dot + '+', Comment.Preproc),
  423. (r'\n', Text, 'root'),
  424. ],
  425. 'filter-block': [
  426. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Name.Decorator),
  427. (r'(#\{)(' + _dot + r'*?)(\})',
  428. bygroups(String.Interpol, using(ScalaLexer), String.Interpol)),
  429. (r'\n', Text, 'root'),
  430. ],
  431. }
  432. class PugLexer(ExtendedRegexLexer):
  433. """
  434. For Pug markup.
  435. Pug is a variant of Scaml, see:
  436. http://scalate.fusesource.org/documentation/scaml-reference.html
  437. .. versionadded:: 1.4
  438. """
  439. name = 'Pug'
  440. aliases = ['pug', 'jade']
  441. filenames = ['*.pug', '*.jade']
  442. mimetypes = ['text/x-pug', 'text/x-jade']
  443. flags = re.IGNORECASE
  444. _dot = r'.'
  445. tokens = {
  446. 'root': [
  447. (r'[ \t]*\n', Text),
  448. (r'[ \t]*', _indentation),
  449. ],
  450. 'css': [
  451. (r'\.[\w:-]+', Name.Class, 'tag'),
  452. (r'\#[\w:-]+', Name.Function, 'tag'),
  453. ],
  454. 'eval-or-plain': [
  455. (r'[&!]?==', Punctuation, 'plain'),
  456. (r'([&!]?[=~])(' + _dot + r'*\n)',
  457. bygroups(Punctuation, using(ScalaLexer)), 'root'),
  458. default('plain'),
  459. ],
  460. 'content': [
  461. include('css'),
  462. (r'!!!' + _dot + r'*\n', Name.Namespace, '#pop'),
  463. (r'(/)(\[' + _dot + r'*?\])(' + _dot + r'*\n)',
  464. bygroups(Comment, Comment.Special, Comment),
  465. '#pop'),
  466. (r'/' + _dot + r'*\n', _starts_block(Comment, 'html-comment-block'),
  467. '#pop'),
  468. (r'-#' + _dot + r'*\n', _starts_block(Comment.Preproc,
  469. 'scaml-comment-block'), '#pop'),
  470. (r'(-@\s*)(import)?(' + _dot + r'*\n)',
  471. bygroups(Punctuation, Keyword, using(ScalaLexer)),
  472. '#pop'),
  473. (r'(-)(' + _dot + r'*\n)',
  474. bygroups(Punctuation, using(ScalaLexer)),
  475. '#pop'),
  476. (r':' + _dot + r'*\n', _starts_block(Name.Decorator, 'filter-block'),
  477. '#pop'),
  478. (r'[\w:-]+', Name.Tag, 'tag'),
  479. (r'\|', Text, 'eval-or-plain'),
  480. ],
  481. 'tag': [
  482. include('css'),
  483. (r'\{(,\n|' + _dot + r')*?\}', using(ScalaLexer)),
  484. (r'\[' + _dot + r'*?\]', using(ScalaLexer)),
  485. (r'\(', Text, 'html-attributes'),
  486. (r'/[ \t]*\n', Punctuation, '#pop:2'),
  487. (r'[<>]{1,2}(?=[ \t=])', Punctuation),
  488. include('eval-or-plain'),
  489. ],
  490. 'plain': [
  491. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Text),
  492. (r'(#\{)(' + _dot + r'*?)(\})',
  493. bygroups(String.Interpol, using(ScalaLexer), String.Interpol)),
  494. (r'\n', Text, 'root'),
  495. ],
  496. 'html-attributes': [
  497. (r'\s+', Text),
  498. (r'[\w:-]+[ \t]*=', Name.Attribute, 'html-attribute-value'),
  499. (r'[\w:-]+', Name.Attribute),
  500. (r'\)', Text, '#pop'),
  501. ],
  502. 'html-attribute-value': [
  503. (r'[ \t]+', Text),
  504. (r'\w+', Name.Variable, '#pop'),
  505. (r'@\w+', Name.Variable.Instance, '#pop'),
  506. (r'\$\w+', Name.Variable.Global, '#pop'),
  507. (r"'(\\\\|\\[^\\]|[^'\\\n])*'", String, '#pop'),
  508. (r'"(\\\\|\\[^\\]|[^"\\\n])*"', String, '#pop'),
  509. ],
  510. 'html-comment-block': [
  511. (_dot + '+', Comment),
  512. (r'\n', Text, 'root'),
  513. ],
  514. 'scaml-comment-block': [
  515. (_dot + '+', Comment.Preproc),
  516. (r'\n', Text, 'root'),
  517. ],
  518. 'filter-block': [
  519. (r'([^#\n]|#[^{\n]|(\\\\)*\\#\{)+', Name.Decorator),
  520. (r'(#\{)(' + _dot + r'*?)(\})',
  521. bygroups(String.Interpol, using(ScalaLexer), String.Interpol)),
  522. (r'\n', Text, 'root'),
  523. ],
  524. }
  525. JadeLexer = PugLexer # compat