Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pygments/lexers/templates.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

828 statements  

1""" 

2 pygments.lexers.templates 

3 ~~~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5 Lexers for various template engines' markup. 

6 

7 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import re 

12 

13from pygments.lexers.html import HtmlLexer, XmlLexer 

14from pygments.lexers.javascript import JavascriptLexer, LassoLexer 

15from pygments.lexers.css import CssLexer 

16from pygments.lexers.php import PhpLexer 

17from pygments.lexers.python import PythonLexer 

18from pygments.lexers.perl import PerlLexer 

19from pygments.lexers.jvm import JavaLexer, TeaLangLexer 

20from pygments.lexers.data import YamlLexer 

21from pygments.lexers.sql import SqlLexer 

22from pygments.lexer import Lexer, DelegatingLexer, RegexLexer, bygroups, \ 

23 include, using, this, default, combined 

24from pygments.token import Error, Punctuation, Whitespace, \ 

25 Text, Comment, Operator, Keyword, Name, String, Number, Other, Token 

26from pygments.util import html_doctype_matches, looks_like_xml 

27 

28__all__ = ['HtmlPhpLexer', 'XmlPhpLexer', 'CssPhpLexer', 

29 'JavascriptPhpLexer', 'ErbLexer', 'RhtmlLexer', 

30 'XmlErbLexer', 'CssErbLexer', 'JavascriptErbLexer', 

31 'SmartyLexer', 'HtmlSmartyLexer', 'XmlSmartyLexer', 

32 'CssSmartyLexer', 'JavascriptSmartyLexer', 'DjangoLexer', 

33 'HtmlDjangoLexer', 'CssDjangoLexer', 'XmlDjangoLexer', 

34 'JavascriptDjangoLexer', 'GenshiLexer', 'HtmlGenshiLexer', 

35 'GenshiTextLexer', 'CssGenshiLexer', 'JavascriptGenshiLexer', 

36 'MyghtyLexer', 'MyghtyHtmlLexer', 'MyghtyXmlLexer', 

37 'MyghtyCssLexer', 'MyghtyJavascriptLexer', 'MasonLexer', 'MakoLexer', 

38 'MakoHtmlLexer', 'MakoXmlLexer', 'MakoJavascriptLexer', 

39 'MakoCssLexer', 'JspLexer', 'CheetahLexer', 'CheetahHtmlLexer', 

40 'CheetahXmlLexer', 'CheetahJavascriptLexer', 'EvoqueLexer', 

41 'EvoqueHtmlLexer', 'EvoqueXmlLexer', 'ColdfusionLexer', 

42 'ColdfusionHtmlLexer', 'ColdfusionCFCLexer', 'VelocityLexer', 

43 'VelocityHtmlLexer', 'VelocityXmlLexer', 'SspLexer', 

44 'TeaTemplateLexer', 'LassoHtmlLexer', 'LassoXmlLexer', 

45 'LassoCssLexer', 'LassoJavascriptLexer', 'HandlebarsLexer', 

46 'HandlebarsHtmlLexer', 'YamlJinjaLexer', 'LiquidLexer', 

47 'TwigLexer', 'TwigHtmlLexer', 'Angular2Lexer', 'Angular2HtmlLexer', 

48 'SqlJinjaLexer'] 

49 

50 

51class ErbLexer(Lexer): 

52 """ 

53 Generic ERB (Ruby Templating) lexer. 

54 

55 Just highlights ruby code between the preprocessor directives, other data 

56 is left untouched by the lexer. 

57 

58 All options are also forwarded to the `RubyLexer`. 

59 """ 

60 

61 name = 'ERB' 

62 url = 'https://github.com/ruby/erb' 

63 aliases = ['erb'] 

64 mimetypes = ['application/x-ruby-templating'] 

65 version_added = '' 

66 

67 _block_re = re.compile(r'(<%%|%%>|<%=|<%#|<%-|<%|-%>|%>|^%[^%].*?$)', re.M) 

68 

69 def __init__(self, **options): 

70 from pygments.lexers.ruby import RubyLexer 

71 self.ruby_lexer = RubyLexer(**options) 

72 Lexer.__init__(self, **options) 

73 

74 def get_tokens_unprocessed(self, text): 

75 """ 

76 Since ERB doesn't allow "<%" and other tags inside of ruby 

77 blocks we have to use a split approach here that fails for 

78 that too. 

79 """ 

80 tokens = self._block_re.split(text) 

81 tokens.reverse() 

82 state = idx = 0 

83 try: 

84 while True: 

85 # text 

86 if state == 0: 

87 val = tokens.pop() 

88 yield idx, Other, val 

89 idx += len(val) 

90 state = 1 

91 # block starts 

92 elif state == 1: 

93 tag = tokens.pop() 

94 # literals 

95 if tag in ('<%%', '%%>'): 

96 yield idx, Other, tag 

97 idx += 3 

98 state = 0 

99 # comment 

100 elif tag == '<%#': 

101 yield idx, Comment.Preproc, tag 

102 val = tokens.pop() 

103 yield idx + 3, Comment, val 

104 idx += 3 + len(val) 

105 state = 2 

106 # blocks or output 

107 elif tag in ('<%', '<%=', '<%-'): 

108 yield idx, Comment.Preproc, tag 

109 idx += len(tag) 

110 data = tokens.pop() 

111 r_idx = 0 

112 for r_idx, r_token, r_value in \ 

113 self.ruby_lexer.get_tokens_unprocessed(data): 

114 yield r_idx + idx, r_token, r_value 

115 idx += len(data) 

116 state = 2 

117 elif tag in ('%>', '-%>'): 

118 yield idx, Error, tag 

119 idx += len(tag) 

120 state = 0 

121 # % raw ruby statements 

122 else: 

123 yield idx, Comment.Preproc, tag[0] 

124 r_idx = 0 

125 for r_idx, r_token, r_value in \ 

126 self.ruby_lexer.get_tokens_unprocessed(tag[1:]): 

127 yield idx + 1 + r_idx, r_token, r_value 

128 idx += len(tag) 

129 state = 0 

130 # block ends 

131 elif state == 2: 

132 tag = tokens.pop() 

133 if tag not in ('%>', '-%>'): 

134 yield idx, Other, tag 

135 else: 

136 yield idx, Comment.Preproc, tag 

137 idx += len(tag) 

138 state = 0 

139 except IndexError: 

140 return 

141 

142 def analyse_text(text): 

143 if '<%' in text and '%>' in text: 

144 return 0.4 

145 

146 

147class SmartyLexer(RegexLexer): 

148 """ 

149 Generic Smarty template lexer. 

150 

151 Just highlights smarty code between the preprocessor directives, other 

152 data is left untouched by the lexer. 

153 """ 

154 

155 name = 'Smarty' 

156 url = 'https://www.smarty.net/' 

157 aliases = ['smarty'] 

158 filenames = ['*.tpl'] 

159 mimetypes = ['application/x-smarty'] 

160 version_added = '' 

161 

162 flags = re.MULTILINE | re.DOTALL 

163 

164 tokens = { 

165 'root': [ 

166 (r'[^{]+', Other), 

167 (r'(\{)(\*.*?\*)(\})', 

168 bygroups(Comment.Preproc, Comment, Comment.Preproc)), 

169 (r'(\{php\})(.*?)(\{/php\})', 

170 bygroups(Comment.Preproc, using(PhpLexer, startinline=True), 

171 Comment.Preproc)), 

172 (r'(\{)(/?[a-zA-Z_]\w*)(\s*)', 

173 bygroups(Comment.Preproc, Name.Function, Text), 'smarty'), 

174 (r'\{', Comment.Preproc, 'smarty') 

175 ], 

176 'smarty': [ 

177 (r'\s+', Text), 

178 (r'\{', Comment.Preproc, '#push'), 

179 (r'\}', Comment.Preproc, '#pop'), 

180 (r'#[a-zA-Z_]\w*#', Name.Variable), 

181 (r'\$[a-zA-Z_]\w*(\.\w+)*', Name.Variable), 

182 (r'[~!%^&*()+=|\[\]:;,.<>/?@-]', Operator), 

183 (r'(true|false|null)\b', Keyword.Constant), 

184 (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|" 

185 r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

186 (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

187 (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

188 (r'[a-zA-Z_]\w*', Name.Attribute) 

189 ] 

190 } 

191 

192 def analyse_text(text): 

193 rv = 0.0 

194 if re.search(r'\{if\s+.*?\}.*?\{/if\}', text): 

195 rv += 0.15 

196 if re.search(r'\{include\s+file=.*?\}', text): 

197 rv += 0.15 

198 if re.search(r'\{foreach\s+.*?\}.*?\{/foreach\}', text): 

199 rv += 0.15 

200 if re.search(r'\{\$.*?\}', text): 

201 rv += 0.01 

202 return rv 

203 

204 

205class VelocityLexer(RegexLexer): 

206 """ 

207 Generic Velocity template lexer. 

208 

209 Just highlights velocity directives and variable references, other 

210 data is left untouched by the lexer. 

211 """ 

212 

213 name = 'Velocity' 

214 url = 'https://velocity.apache.org/' 

215 aliases = ['velocity'] 

216 filenames = ['*.vm', '*.fhtml'] 

217 version_added = '' 

218 

219 flags = re.MULTILINE | re.DOTALL 

220 

221 identifier = r'[a-zA-Z_]\w*' 

222 

223 tokens = { 

224 'root': [ 

225 (r'[^{#$]+', Other), 

226 (r'(#)(\*.*?\*)(#)', 

227 bygroups(Comment.Preproc, Comment, Comment.Preproc)), 

228 (r'(##)(.*?$)', 

229 bygroups(Comment.Preproc, Comment)), 

230 (r'(#\{?)(' + identifier + r')(\}?)(\s?\()', 

231 bygroups(Comment.Preproc, Name.Function, Comment.Preproc, Punctuation), 

232 'directiveparams'), 

233 (r'(#\{?)(' + identifier + r')(\}|\b)', 

234 bygroups(Comment.Preproc, Name.Function, Comment.Preproc)), 

235 (r'\$!?\{?', Punctuation, 'variable') 

236 ], 

237 'variable': [ 

238 (identifier, Name.Variable), 

239 (r'\(', Punctuation, 'funcparams'), 

240 (r'(\.)(' + identifier + r')', 

241 bygroups(Punctuation, Name.Variable), '#push'), 

242 (r'\}', Punctuation, '#pop'), 

243 default('#pop') 

244 ], 

245 'directiveparams': [ 

246 (r'(&&|\|\||==?|!=?|[-<>+*%&|^/])|\b(eq|ne|gt|lt|ge|le|not|in)\b', 

247 Operator), 

248 (r'\[', Operator, 'rangeoperator'), 

249 (r'\b' + identifier + r'\b', Name.Function), 

250 include('funcparams') 

251 ], 

252 'rangeoperator': [ 

253 (r'\.\.', Operator), 

254 include('funcparams'), 

255 (r'\]', Operator, '#pop') 

256 ], 

257 'funcparams': [ 

258 (r'\$!?\{?', Punctuation, 'variable'), 

259 (r'\s+', Text), 

260 (r'[,:]', Punctuation), 

261 (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

262 (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

263 (r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

264 (r"\b[0-9]+\b", Number), 

265 (r'(true|false|null)\b', Keyword.Constant), 

266 (r'\(', Punctuation, '#push'), 

267 (r'\)', Punctuation, '#pop'), 

268 (r'\{', Punctuation, '#push'), 

269 (r'\}', Punctuation, '#pop'), 

270 (r'\[', Punctuation, '#push'), 

271 (r'\]', Punctuation, '#pop'), 

272 ] 

273 } 

274 

275 def analyse_text(text): 

276 rv = 0.0 

277 if re.search(r'#\{?macro\}?\(.*?\).*?#\{?end\}?', text, re.DOTALL): 

278 rv += 0.25 

279 if re.search(r'#\{?if\}?\(.+?\).*?#\{?end\}?', text, re.DOTALL): 

280 rv += 0.15 

281 if re.search(r'#\{?foreach\}?\(.+?\).*?#\{?end\}?', text, re.DOTALL): 

282 rv += 0.15 

283 if re.search(r'\$!?\{?[a-zA-Z_]\w*(\([^)]*\))?' 

284 r'(\.\w+(\([^)]*\))?)*\}?', text): 

285 rv += 0.01 

286 return rv 

287 

288 

289class VelocityHtmlLexer(DelegatingLexer): 

290 """ 

291 Subclass of the `VelocityLexer` that highlights unlexed data 

292 with the `HtmlLexer`. 

293 

294 """ 

295 

296 name = 'HTML+Velocity' 

297 aliases = ['html+velocity'] 

298 version_added = '' 

299 alias_filenames = ['*.html', '*.fhtml'] 

300 mimetypes = ['text/html+velocity'] 

301 url = 'https://velocity.apache.org/' 

302 

303 def __init__(self, **options): 

304 super().__init__(HtmlLexer, VelocityLexer, **options) 

305 

306 

307class VelocityXmlLexer(DelegatingLexer): 

308 """ 

309 Subclass of the `VelocityLexer` that highlights unlexed data 

310 with the `XmlLexer`. 

311 

312 """ 

313 

314 name = 'XML+Velocity' 

315 aliases = ['xml+velocity'] 

316 version_added = '' 

317 alias_filenames = ['*.xml', '*.vm'] 

318 mimetypes = ['application/xml+velocity'] 

319 url = 'https://velocity.apache.org/' 

320 

321 def __init__(self, **options): 

322 super().__init__(XmlLexer, VelocityLexer, **options) 

323 

324 def analyse_text(text): 

325 rv = VelocityLexer.analyse_text(text) - 0.01 

326 if looks_like_xml(text): 

327 rv += 0.4 

328 return rv 

329 

330 

331class DjangoLexer(RegexLexer): 

332 """ 

333 Generic `Django <https://www.djangoproject.com/documentation/templates/>`_ 

334 and `Jinja <https://jinja.palletsprojects.com>`_ template lexer. 

335 

336 It just highlights django/jinja code between the preprocessor directives, 

337 other data is left untouched by the lexer. 

338 """ 

339 

340 name = 'Django/Jinja' 

341 aliases = ['django', 'jinja'] 

342 mimetypes = ['application/x-django-templating', 'application/x-jinja'] 

343 url = 'https://www.djangoproject.com/documentation/templates' 

344 version_added = '' 

345 

346 flags = re.M | re.S 

347 

348 tokens = { 

349 'root': [ 

350 (r'[^{]+', Other), 

351 (r'\{\{', Comment.Preproc, 'var'), 

352 # jinja/django comments 

353 (r'\{#.*?#\}', Comment), 

354 # django comments 

355 (r'(\{%)(-?\s*)(comment)(\s*-?)(%\})(.*?)' 

356 r'(\{%)(-?\s*)(endcomment)(\s*-?)(%\})', 

357 bygroups(Comment.Preproc, Text, Keyword, Text, Comment.Preproc, 

358 Comment, Comment.Preproc, Text, Keyword, Text, 

359 Comment.Preproc)), 

360 # raw jinja blocks 

361 (r'(\{%)(-?\s*)(raw)(\s*-?)(%\})(.*?)' 

362 r'(\{%)(-?\s*)(endraw)(\s*-?)(%\})', 

363 bygroups(Comment.Preproc, Text, Keyword, Text, Comment.Preproc, 

364 Text, Comment.Preproc, Text, Keyword, Text, 

365 Comment.Preproc)), 

366 # filter blocks 

367 (r'(\{%)(-?\s*)(filter)(\s+)([a-zA-Z_]\w*)', 

368 bygroups(Comment.Preproc, Text, Keyword, Text, Name.Function), 

369 'block'), 

370 (r'(\{%)(-?\s*)([a-zA-Z_]\w*)', 

371 bygroups(Comment.Preproc, Text, Keyword), 'block'), 

372 (r'\{', Other) 

373 ], 

374 'varnames': [ 

375 (r'(\|)(\s*)([a-zA-Z_]\w*)', 

376 bygroups(Operator, Text, Name.Function)), 

377 (r'(is)(\s+)(not)?(\s+)?([a-zA-Z_]\w*)', 

378 bygroups(Keyword, Text, Keyword, Text, Name.Function)), 

379 (r'(_|true|false|none|True|False|None)\b', Keyword.Pseudo), 

380 (r'(in|as|reversed|recursive|not|and|or|is|if|else|import|' 

381 r'with(?:(?:out)?\s*context)?|scoped|ignore\s+missing)\b', 

382 Keyword), 

383 (r'(loop|block|super|forloop)\b', Name.Builtin), 

384 (r'[a-zA-Z_][\w-]*', Name.Variable), 

385 (r'\.\w+', Name.Variable), 

386 (r':?"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

387 (r":?'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

388 (r'([{}()\[\]+\-*/%,:~]|[><=]=?|!=)', Operator), 

389 (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|" 

390 r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

391 ], 

392 'var': [ 

393 (r'\s+', Text), 

394 (r'(-?)(\}\})', bygroups(Text, Comment.Preproc), '#pop'), 

395 include('varnames') 

396 ], 

397 'block': [ 

398 (r'\s+', Text), 

399 (r'(-?)(%\})', bygroups(Text, Comment.Preproc), '#pop'), 

400 include('varnames'), 

401 (r'.', Punctuation) 

402 ] 

403 } 

404 

405 def analyse_text(text): 

406 rv = 0.0 

407 if re.search(r'\{%\s*(block|extends)', text) is not None: 

408 rv += 0.4 

409 if re.search(r'\{%\s*if\s*.*?%\}', text) is not None: 

410 rv += 0.1 

411 if re.search(r'\{\{.*?\}\}', text) is not None: 

412 rv += 0.1 

413 return rv 

414 

415 

416class MyghtyLexer(RegexLexer): 

417 """ 

418 Generic myghty templates lexer. Code that isn't Myghty 

419 markup is yielded as `Token.Other`. 

420 """ 

421 

422 name = 'Myghty' 

423 url = 'http://www.myghty.org/' 

424 aliases = ['myghty'] 

425 filenames = ['*.myt', 'autodelegate'] 

426 mimetypes = ['application/x-myghty'] 

427 version_added = '0.6' 

428 

429 tokens = { 

430 'root': [ 

431 (r'\s+', Text), 

432 (r'(?s)(<%(?:def|method))(\s*)(.*?)(>)(.*?)(</%\2\s*>)', 

433 bygroups(Name.Tag, Text, Name.Function, Name.Tag, 

434 using(this), Name.Tag)), 

435 (r'(?s)(<%\w+)(.*?)(>)(.*?)(</%\2\s*>)', 

436 bygroups(Name.Tag, Name.Function, Name.Tag, 

437 using(PythonLexer), Name.Tag)), 

438 (r'(<&[^|])(.*?)(,.*?)?(&>)', 

439 bygroups(Name.Tag, Name.Function, using(PythonLexer), Name.Tag)), 

440 (r'(?s)(<&\|)(.*?)(,.*?)?(&>)', 

441 bygroups(Name.Tag, Name.Function, using(PythonLexer), Name.Tag)), 

442 (r'</&>', Name.Tag), 

443 (r'(?s)(<%!?)(.*?)(%>)', 

444 bygroups(Name.Tag, using(PythonLexer), Name.Tag)), 

445 (r'(?<=^)#[^\n]*(\n|\Z)', Comment), 

446 (r'(?<=^)(%)([^\n]*)(\n|\Z)', 

447 bygroups(Name.Tag, using(PythonLexer), Other)), 

448 (r"""(?sx) 

449 (.+?) # anything, followed by: 

450 (?: 

451 (?<=\n)(?=[%#]) | # an eval or comment line 

452 (?=</?[%&]) | # a substitution or block or 

453 # call start or end 

454 # - don't consume 

455 (\\\n) | # an escaped newline 

456 \Z # end of string 

457 )""", bygroups(Other, Operator)), 

458 ] 

459 } 

460 

461 

462class MyghtyHtmlLexer(DelegatingLexer): 

463 """ 

464 Subclass of the `MyghtyLexer` that highlights unlexed data 

465 with the `HtmlLexer`. 

466 """ 

467 

468 name = 'HTML+Myghty' 

469 aliases = ['html+myghty'] 

470 mimetypes = ['text/html+myghty'] 

471 url = 'http://www.myghty.org/' 

472 version_added = '0.6' 

473 

474 def __init__(self, **options): 

475 super().__init__(HtmlLexer, MyghtyLexer, **options) 

476 

477 

478class MyghtyXmlLexer(DelegatingLexer): 

479 """ 

480 Subclass of the `MyghtyLexer` that highlights unlexed data 

481 with the `XmlLexer`. 

482 """ 

483 

484 name = 'XML+Myghty' 

485 aliases = ['xml+myghty'] 

486 mimetypes = ['application/xml+myghty'] 

487 url = 'http://www.myghty.org/' 

488 version_added = '0.6' 

489 

490 def __init__(self, **options): 

491 super().__init__(XmlLexer, MyghtyLexer, **options) 

492 

493 

494class MyghtyJavascriptLexer(DelegatingLexer): 

495 """ 

496 Subclass of the `MyghtyLexer` that highlights unlexed data 

497 with the `JavascriptLexer`. 

498 """ 

499 

500 name = 'JavaScript+Myghty' 

501 aliases = ['javascript+myghty', 'js+myghty'] 

502 mimetypes = ['application/x-javascript+myghty', 

503 'text/x-javascript+myghty', 

504 'text/javascript+mygthy'] 

505 url = 'http://www.myghty.org/' 

506 version_added = '0.6' 

507 

508 def __init__(self, **options): 

509 super().__init__(JavascriptLexer, MyghtyLexer, **options) 

510 

511 

512class MyghtyCssLexer(DelegatingLexer): 

513 """ 

514 Subclass of the `MyghtyLexer` that highlights unlexed data 

515 with the `CssLexer`. 

516 """ 

517 

518 name = 'CSS+Myghty' 

519 aliases = ['css+myghty'] 

520 mimetypes = ['text/css+myghty'] 

521 url = 'http://www.myghty.org/' 

522 version_added = '0.6' 

523 

524 def __init__(self, **options): 

525 super().__init__(CssLexer, MyghtyLexer, **options) 

526 

527 

528class MasonLexer(RegexLexer): 

529 """ 

530 Generic mason templates lexer. Stolen from Myghty lexer. Code that isn't 

531 Mason markup is HTML. 

532 """ 

533 name = 'Mason' 

534 url = 'http://www.masonhq.com/' 

535 aliases = ['mason'] 

536 filenames = ['*.m', '*.mhtml', '*.mc', '*.mi', 'autohandler', 'dhandler'] 

537 mimetypes = ['application/x-mason'] 

538 version_added = '1.4' 

539 

540 tokens = { 

541 'root': [ 

542 (r'\s+', Whitespace), 

543 (r'(?s)(<%doc>)(.*?)(</%doc>)', 

544 bygroups(Name.Tag, Comment.Multiline, Name.Tag)), 

545 (r'(?s)(<%(?:def|method))(\s*)(.*?)(>)(.*?)(</%\2\s*>)', 

546 bygroups(Name.Tag, Whitespace, Name.Function, Name.Tag, 

547 using(this), Name.Tag)), 

548 (r'(?s)(<%(\w+)(.*?)(>))(.*?)(</%\2\s*>)', 

549 bygroups(Name.Tag, None, None, None, using(PerlLexer), Name.Tag)), 

550 (r'(?s)(<&[^|])(.*?)(,.*?)?(&>)', 

551 bygroups(Name.Tag, Name.Function, using(PerlLexer), Name.Tag)), 

552 (r'(?s)(<&\|)(.*?)(,.*?)?(&>)', 

553 bygroups(Name.Tag, Name.Function, using(PerlLexer), Name.Tag)), 

554 (r'</&>', Name.Tag), 

555 (r'(?s)(<%!?)(.*?)(%>)', 

556 bygroups(Name.Tag, using(PerlLexer), Name.Tag)), 

557 (r'(?<=^)#[^\n]*(\n|\Z)', Comment), 

558 (r'(?<=^)(%)([^\n]*)(\n|\Z)', 

559 bygroups(Name.Tag, using(PerlLexer), Other)), 

560 (r"""(?sx) 

561 (.+?) # anything, followed by: 

562 (?: 

563 (?<=\n)(?=[%#]) | # an eval or comment line 

564 (?=</?[%&]) | # a substitution or block or 

565 # call start or end 

566 # - don't consume 

567 (\\\n) | # an escaped newline 

568 \Z # end of string 

569 )""", bygroups(using(HtmlLexer), Operator)), 

570 ] 

571 } 

572 

573 def analyse_text(text): 

574 result = 0.0 

575 if re.search(r'</%(class|doc|init)>', text) is not None: 

576 result = 1.0 

577 elif re.search(r'<&.+&>', text, re.DOTALL) is not None: 

578 result = 0.11 

579 return result 

580 

581 

582class MakoLexer(RegexLexer): 

583 """ 

584 Generic mako templates lexer. Code that isn't Mako 

585 markup is yielded as `Token.Other`. 

586 """ 

587 

588 name = 'Mako' 

589 url = 'http://www.makotemplates.org/' 

590 aliases = ['mako'] 

591 filenames = ['*.mao'] 

592 mimetypes = ['application/x-mako'] 

593 version_added = '0.7' 

594 

595 tokens = { 

596 'root': [ 

597 (r'(\s*)(%)(\s*end(?:\w+))(\n|\Z)', 

598 bygroups(Text.Whitespace, Comment.Preproc, Keyword, Other)), 

599 (r'(\s*)(%)([^\n]*)(\n|\Z)', 

600 bygroups(Text.Whitespace, Comment.Preproc, using(PythonLexer), Other)), 

601 (r'(\s*)(##[^\n]*)(\n|\Z)', 

602 bygroups(Text.Whitespace, Comment.Single, Text.Whitespace)), 

603 (r'(?s)<%doc>.*?</%doc>', Comment.Multiline), 

604 (r'(<%)([\w.:]+)', 

605 bygroups(Comment.Preproc, Name.Builtin), 'tag'), 

606 (r'(</%)([\w.:]+)(>)', 

607 bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc)), 

608 (r'<%(?=([\w.:]+))', Comment.Preproc, 'ondeftags'), 

609 (r'(?s)(<%(?:!?))(.*?)(%>)', 

610 bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)), 

611 (r'(\$\{)(.*?)(\})', 

612 bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)), 

613 (r'''(?sx) 

614 (.+?) # anything, followed by: 

615 (?: 

616 (?<=\n)(?=%|\#\#) | # an eval or comment line 

617 (?=\#\*) | # multiline comment 

618 (?=</?%) | # a python block 

619 # call start or end 

620 (?=\$\{) | # a substitution 

621 (?<=\n)(?=\s*%) | 

622 # - don't consume 

623 (\\\n) | # an escaped newline 

624 \Z # end of string 

625 ) 

626 ''', bygroups(Other, Operator)), 

627 (r'\s+', Text), 

628 ], 

629 'ondeftags': [ 

630 (r'<%', Comment.Preproc), 

631 (r'(?<=<%)(include|inherit|namespace|page)', Name.Builtin), 

632 include('tag'), 

633 ], 

634 'tag': [ 

635 (r'((?:\w+)\s*=)(\s*)(".*?")', 

636 bygroups(Name.Attribute, Text, String)), 

637 (r'/?\s*>', Comment.Preproc, '#pop'), 

638 (r'\s+', Text), 

639 ], 

640 'attr': [ 

641 ('".*?"', String, '#pop'), 

642 ("'.*?'", String, '#pop'), 

643 (r'[^\s>]+', String, '#pop'), 

644 ], 

645 } 

646 

647 

648class MakoHtmlLexer(DelegatingLexer): 

649 """ 

650 Subclass of the `MakoLexer` that highlights unlexed data 

651 with the `HtmlLexer`. 

652 """ 

653 

654 name = 'HTML+Mako' 

655 aliases = ['html+mako'] 

656 mimetypes = ['text/html+mako'] 

657 url = 'http://www.makotemplates.org/' 

658 version_added = '0.7' 

659 

660 def __init__(self, **options): 

661 super().__init__(HtmlLexer, MakoLexer, **options) 

662 

663 

664class MakoXmlLexer(DelegatingLexer): 

665 """ 

666 Subclass of the `MakoLexer` that highlights unlexed data 

667 with the `XmlLexer`. 

668 """ 

669 

670 name = 'XML+Mako' 

671 aliases = ['xml+mako'] 

672 mimetypes = ['application/xml+mako'] 

673 url = 'http://www.makotemplates.org/' 

674 version_added = '0.7' 

675 

676 def __init__(self, **options): 

677 super().__init__(XmlLexer, MakoLexer, **options) 

678 

679 

680class MakoJavascriptLexer(DelegatingLexer): 

681 """ 

682 Subclass of the `MakoLexer` that highlights unlexed data 

683 with the `JavascriptLexer`. 

684 """ 

685 

686 name = 'JavaScript+Mako' 

687 aliases = ['javascript+mako', 'js+mako'] 

688 mimetypes = ['application/x-javascript+mako', 

689 'text/x-javascript+mako', 

690 'text/javascript+mako'] 

691 url = 'http://www.makotemplates.org/' 

692 version_added = '0.7' 

693 

694 def __init__(self, **options): 

695 super().__init__(JavascriptLexer, MakoLexer, **options) 

696 

697 

698class MakoCssLexer(DelegatingLexer): 

699 """ 

700 Subclass of the `MakoLexer` that highlights unlexed data 

701 with the `CssLexer`. 

702 """ 

703 

704 name = 'CSS+Mako' 

705 aliases = ['css+mako'] 

706 mimetypes = ['text/css+mako'] 

707 url = 'http://www.makotemplates.org/' 

708 version_added = '0.7' 

709 

710 def __init__(self, **options): 

711 super().__init__(CssLexer, MakoLexer, **options) 

712 

713 

714# Genshi and Cheetah lexers courtesy of Matt Good. 

715 

716class CheetahPythonLexer(Lexer): 

717 """ 

718 Lexer for handling Cheetah's special $ tokens in Python syntax. 

719 """ 

720 

721 def get_tokens_unprocessed(self, text): 

722 pylexer = PythonLexer(**self.options) 

723 for pos, type_, value in pylexer.get_tokens_unprocessed(text): 

724 if type_ == Token.Error and value == '$': 

725 type_ = Comment.Preproc 

726 yield pos, type_, value 

727 

728 

729class CheetahLexer(RegexLexer): 

730 """ 

731 Generic cheetah templates lexer. Code that isn't Cheetah 

732 markup is yielded as `Token.Other`. This also works for 

733 `spitfire templates`_ which use the same syntax. 

734 

735 .. _spitfire templates: http://code.google.com/p/spitfire/ 

736 """ 

737 

738 name = 'Cheetah' 

739 url = 'http://www.cheetahtemplate.org/' 

740 aliases = ['cheetah', 'spitfire'] 

741 filenames = ['*.tmpl', '*.spt'] 

742 mimetypes = ['application/x-cheetah', 'application/x-spitfire'] 

743 version_added = '' 

744 

745 tokens = { 

746 'root': [ 

747 (r'(##[^\n]*)$', 

748 (bygroups(Comment))), 

749 (r'#[*](.|\n)*?[*]#', Comment), 

750 (r'#end[^#\n]*(?:#|$)', Comment.Preproc), 

751 (r'#slurp$', Comment.Preproc), 

752 (r'(#[a-zA-Z]+)([^#\n]*)(#|$)', 

753 (bygroups(Comment.Preproc, using(CheetahPythonLexer), 

754 Comment.Preproc))), 

755 # TODO support other Python syntax like $foo['bar'] 

756 (r'(\$)([a-zA-Z_][\w.]*\w)', 

757 bygroups(Comment.Preproc, using(CheetahPythonLexer))), 

758 (r'(?s)(\$\{!?)(.*?)(\})', 

759 bygroups(Comment.Preproc, using(CheetahPythonLexer), 

760 Comment.Preproc)), 

761 (r'''(?sx) 

762 (.+?) # anything, followed by: 

763 (?: 

764 (?=\#[#a-zA-Z]*) | # an eval comment 

765 (?=\$[a-zA-Z_{]) | # a substitution 

766 \Z # end of string 

767 ) 

768 ''', Other), 

769 (r'\s+', Text), 

770 ], 

771 } 

772 

773 

774class CheetahHtmlLexer(DelegatingLexer): 

775 """ 

776 Subclass of the `CheetahLexer` that highlights unlexed data 

777 with the `HtmlLexer`. 

778 """ 

779 

780 name = 'HTML+Cheetah' 

781 aliases = ['html+cheetah', 'html+spitfire', 'htmlcheetah'] 

782 mimetypes = ['text/html+cheetah', 'text/html+spitfire'] 

783 url = 'http://www.cheetahtemplate.org/' 

784 version_added = '' 

785 

786 def __init__(self, **options): 

787 super().__init__(HtmlLexer, CheetahLexer, **options) 

788 

789 

790class CheetahXmlLexer(DelegatingLexer): 

791 """ 

792 Subclass of the `CheetahLexer` that highlights unlexed data 

793 with the `XmlLexer`. 

794 """ 

795 

796 name = 'XML+Cheetah' 

797 aliases = ['xml+cheetah', 'xml+spitfire'] 

798 mimetypes = ['application/xml+cheetah', 'application/xml+spitfire'] 

799 url = 'http://www.cheetahtemplate.org/' 

800 version_added = '' 

801 

802 def __init__(self, **options): 

803 super().__init__(XmlLexer, CheetahLexer, **options) 

804 

805 

806class CheetahJavascriptLexer(DelegatingLexer): 

807 """ 

808 Subclass of the `CheetahLexer` that highlights unlexed data 

809 with the `JavascriptLexer`. 

810 """ 

811 

812 name = 'JavaScript+Cheetah' 

813 aliases = ['javascript+cheetah', 'js+cheetah', 

814 'javascript+spitfire', 'js+spitfire'] 

815 mimetypes = ['application/x-javascript+cheetah', 

816 'text/x-javascript+cheetah', 

817 'text/javascript+cheetah', 

818 'application/x-javascript+spitfire', 

819 'text/x-javascript+spitfire', 

820 'text/javascript+spitfire'] 

821 url = 'http://www.cheetahtemplate.org/' 

822 version_added = '' 

823 

824 def __init__(self, **options): 

825 super().__init__(JavascriptLexer, CheetahLexer, **options) 

826 

827 

828class GenshiTextLexer(RegexLexer): 

829 """ 

830 A lexer that highlights genshi text templates. 

831 """ 

832 

833 name = 'Genshi Text' 

834 url = 'https://genshi.edgewall.org/' 

835 aliases = ['genshitext'] 

836 mimetypes = ['application/x-genshi-text', 'text/x-genshi'] 

837 version_added = '' 

838 

839 tokens = { 

840 'root': [ 

841 (r'[^#$\s]+', Other), 

842 (r'^(\s*)(##.*)$', bygroups(Text, Comment)), 

843 (r'^(\s*)(#)', bygroups(Text, Comment.Preproc), 'directive'), 

844 include('variable'), 

845 (r'[#$\s]', Other), 

846 ], 

847 'directive': [ 

848 (r'\n', Text, '#pop'), 

849 (r'(?:def|for|if)\s+.*', using(PythonLexer), '#pop'), 

850 (r'(choose|when|with)([^\S\n]+)(.*)', 

851 bygroups(Keyword, Text, using(PythonLexer)), '#pop'), 

852 (r'(choose|otherwise)\b', Keyword, '#pop'), 

853 (r'(end\w*)([^\S\n]*)(.*)', bygroups(Keyword, Text, Comment), '#pop'), 

854 ], 

855 'variable': [ 

856 (r'(?<!\$)(\$\{)(.+?)(\})', 

857 bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)), 

858 (r'(?<!\$)(\$)([a-zA-Z_][\w.]*)', 

859 Name.Variable), 

860 ] 

861 } 

862 

863 

864class GenshiMarkupLexer(RegexLexer): 

865 """ 

866 Base lexer for Genshi markup, used by `HtmlGenshiLexer` and 

867 `GenshiLexer`. 

868 """ 

869 

870 flags = re.DOTALL 

871 

872 tokens = { 

873 'root': [ 

874 (r'[^<$]+', Other), 

875 (r'(<\?python)(.*?)(\?>)', 

876 bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)), 

877 # yield style and script blocks as Other 

878 (r'<\s*(script|style)\s*.*?>.*?<\s*/\1\s*>', Other), 

879 (r'<\s*py:[a-zA-Z0-9]+', Name.Tag, 'pytag'), 

880 (r'<\s*[a-zA-Z0-9:.]+', Name.Tag, 'tag'), 

881 include('variable'), 

882 (r'[<$]', Other), 

883 ], 

884 'pytag': [ 

885 (r'\s+', Text), 

886 (r'[\w:-]+\s*=', Name.Attribute, 'pyattr'), 

887 (r'/?\s*>', Name.Tag, '#pop'), 

888 ], 

889 'pyattr': [ 

890 ('(")(.*?)(")', bygroups(String, using(PythonLexer), String), '#pop'), 

891 ("(')(.*?)(')", bygroups(String, using(PythonLexer), String), '#pop'), 

892 (r'[^\s>]+', String, '#pop'), 

893 ], 

894 'tag': [ 

895 (r'\s+', Text), 

896 (r'py:[\w-]+\s*=', Name.Attribute, 'pyattr'), 

897 (r'[\w:-]+\s*=', Name.Attribute, 'attr'), 

898 (r'/?\s*>', Name.Tag, '#pop'), 

899 ], 

900 'attr': [ 

901 ('"', String, 'attr-dstring'), 

902 ("'", String, 'attr-sstring'), 

903 (r'[^\s>]*', String, '#pop') 

904 ], 

905 'attr-dstring': [ 

906 ('"', String, '#pop'), 

907 include('strings'), 

908 ("'", String) 

909 ], 

910 'attr-sstring': [ 

911 ("'", String, '#pop'), 

912 include('strings'), 

913 ("'", String) 

914 ], 

915 'strings': [ 

916 ('[^"\'$]+', String), 

917 include('variable') 

918 ], 

919 'variable': [ 

920 (r'(?<!\$)(\$\{)(.+?)(\})', 

921 bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc)), 

922 (r'(?<!\$)(\$)([a-zA-Z_][\w\.]*)', 

923 Name.Variable), 

924 ] 

925 } 

926 

927 

928class HtmlGenshiLexer(DelegatingLexer): 

929 """ 

930 A lexer that highlights `genshi <https://genshi.edgewall.org/>`_ and 

931 `kid <http://kid-templating.org/>`_ kid HTML templates. 

932 """ 

933 

934 name = 'HTML+Genshi' 

935 aliases = ['html+genshi', 'html+kid'] 

936 version_added = '' 

937 alias_filenames = ['*.html', '*.htm', '*.xhtml'] 

938 mimetypes = ['text/html+genshi'] 

939 url = 'https://genshi.edgewall.org/' 

940 

941 def __init__(self, **options): 

942 super().__init__(HtmlLexer, GenshiMarkupLexer, **options) 

943 

944 def analyse_text(text): 

945 rv = 0.0 

946 if re.search(r'\$\{.*?\}', text) is not None: 

947 rv += 0.2 

948 if re.search(r'py:(.*?)=["\']', text) is not None: 

949 rv += 0.2 

950 return rv + HtmlLexer.analyse_text(text) - 0.01 

951 

952 

953class GenshiLexer(DelegatingLexer): 

954 """ 

955 A lexer that highlights `genshi <https://genshi.edgewall.org/>`_ and 

956 `kid <http://kid-templating.org/>`_ kid XML templates. 

957 """ 

958 

959 name = 'Genshi' 

960 aliases = ['genshi', 'kid', 'xml+genshi', 'xml+kid'] 

961 filenames = ['*.kid'] 

962 version_added = '' 

963 alias_filenames = ['*.xml'] 

964 mimetypes = ['application/x-genshi', 'application/x-kid'] 

965 url = 'https://genshi.edgewall.org/' 

966 

967 def __init__(self, **options): 

968 super().__init__(XmlLexer, GenshiMarkupLexer, **options) 

969 

970 def analyse_text(text): 

971 rv = 0.0 

972 if re.search(r'\$\{.*?\}', text) is not None: 

973 rv += 0.2 

974 if re.search(r'py:(.*?)=["\']', text) is not None: 

975 rv += 0.2 

976 return rv + XmlLexer.analyse_text(text) - 0.01 

977 

978 

979class JavascriptGenshiLexer(DelegatingLexer): 

980 """ 

981 A lexer that highlights javascript code in genshi text templates. 

982 """ 

983 

984 name = 'JavaScript+Genshi Text' 

985 aliases = ['js+genshitext', 'js+genshi', 'javascript+genshitext', 

986 'javascript+genshi'] 

987 version_added = '' 

988 alias_filenames = ['*.js'] 

989 mimetypes = ['application/x-javascript+genshi', 

990 'text/x-javascript+genshi', 

991 'text/javascript+genshi'] 

992 url = 'https://genshi.edgewall.org' 

993 

994 def __init__(self, **options): 

995 super().__init__(JavascriptLexer, GenshiTextLexer, **options) 

996 

997 def analyse_text(text): 

998 return GenshiLexer.analyse_text(text) - 0.05 

999 

1000 

1001class CssGenshiLexer(DelegatingLexer): 

1002 """ 

1003 A lexer that highlights CSS definitions in genshi text templates. 

1004 """ 

1005 

1006 name = 'CSS+Genshi Text' 

1007 aliases = ['css+genshitext', 'css+genshi'] 

1008 version_added = '' 

1009 alias_filenames = ['*.css'] 

1010 mimetypes = ['text/css+genshi'] 

1011 url = 'https://genshi.edgewall.org' 

1012 

1013 def __init__(self, **options): 

1014 super().__init__(CssLexer, GenshiTextLexer, **options) 

1015 

1016 def analyse_text(text): 

1017 return GenshiLexer.analyse_text(text) - 0.05 

1018 

1019 

1020class RhtmlLexer(DelegatingLexer): 

1021 """ 

1022 Subclass of the ERB lexer that highlights the unlexed data with the 

1023 html lexer. 

1024 

1025 Nested Javascript and CSS is highlighted too. 

1026 """ 

1027 

1028 name = 'RHTML' 

1029 aliases = ['rhtml', 'html+erb', 'html+ruby'] 

1030 filenames = ['*.rhtml'] 

1031 version_added = '' 

1032 alias_filenames = ['*.html', '*.htm', '*.xhtml'] 

1033 mimetypes = ['text/html+ruby'] 

1034 url = 'https://github.com/ruby/erb' 

1035 

1036 

1037 def __init__(self, **options): 

1038 super().__init__(HtmlLexer, ErbLexer, **options) 

1039 

1040 def analyse_text(text): 

1041 rv = ErbLexer.analyse_text(text) - 0.01 

1042 if html_doctype_matches(text): 

1043 # one more than the XmlErbLexer returns 

1044 rv += 0.5 

1045 return rv 

1046 

1047 

1048class XmlErbLexer(DelegatingLexer): 

1049 """ 

1050 Subclass of `ErbLexer` which highlights data outside preprocessor 

1051 directives with the `XmlLexer`. 

1052 """ 

1053 

1054 name = 'XML+Ruby' 

1055 aliases = ['xml+ruby', 'xml+erb'] 

1056 version_added = '' 

1057 alias_filenames = ['*.xml'] 

1058 mimetypes = ['application/xml+ruby'] 

1059 url = 'https://github.com/ruby/erb' 

1060 

1061 def __init__(self, **options): 

1062 super().__init__(XmlLexer, ErbLexer, **options) 

1063 

1064 def analyse_text(text): 

1065 rv = ErbLexer.analyse_text(text) - 0.01 

1066 if looks_like_xml(text): 

1067 rv += 0.4 

1068 return rv 

1069 

1070 

1071class CssErbLexer(DelegatingLexer): 

1072 """ 

1073 Subclass of `ErbLexer` which highlights unlexed data with the `CssLexer`. 

1074 """ 

1075 

1076 name = 'CSS+Ruby' 

1077 aliases = ['css+ruby', 'css+erb'] 

1078 version_added = '' 

1079 alias_filenames = ['*.css'] 

1080 mimetypes = ['text/css+ruby'] 

1081 url = 'https://github.com/ruby/erb' 

1082 

1083 def __init__(self, **options): 

1084 super().__init__(CssLexer, ErbLexer, **options) 

1085 

1086 def analyse_text(text): 

1087 return ErbLexer.analyse_text(text) - 0.05 

1088 

1089 

1090class JavascriptErbLexer(DelegatingLexer): 

1091 """ 

1092 Subclass of `ErbLexer` which highlights unlexed data with the 

1093 `JavascriptLexer`. 

1094 """ 

1095 

1096 name = 'JavaScript+Ruby' 

1097 aliases = ['javascript+ruby', 'js+ruby', 'javascript+erb', 'js+erb'] 

1098 version_added = '' 

1099 alias_filenames = ['*.js'] 

1100 mimetypes = ['application/x-javascript+ruby', 

1101 'text/x-javascript+ruby', 

1102 'text/javascript+ruby'] 

1103 url = 'https://github.com/ruby/erb' 

1104 

1105 def __init__(self, **options): 

1106 super().__init__(JavascriptLexer, ErbLexer, **options) 

1107 

1108 def analyse_text(text): 

1109 return ErbLexer.analyse_text(text) - 0.05 

1110 

1111 

1112class HtmlPhpLexer(DelegatingLexer): 

1113 """ 

1114 Subclass of `PhpLexer` that highlights unhandled data with the `HtmlLexer`. 

1115 

1116 Nested Javascript and CSS is highlighted too. 

1117 """ 

1118 

1119 name = 'HTML+PHP' 

1120 aliases = ['html+php'] 

1121 filenames = ['*.phtml'] 

1122 version_added = '' 

1123 alias_filenames = ['*.php', '*.html', '*.htm', '*.xhtml', 

1124 '*.php[345]'] 

1125 mimetypes = ['application/x-php', 

1126 'application/x-httpd-php', 'application/x-httpd-php3', 

1127 'application/x-httpd-php4', 'application/x-httpd-php5'] 

1128 url = 'https://www.php.net' 

1129 

1130 

1131 def __init__(self, **options): 

1132 super().__init__(HtmlLexer, PhpLexer, **options) 

1133 

1134 def analyse_text(text): 

1135 rv = PhpLexer.analyse_text(text) - 0.01 

1136 if html_doctype_matches(text): 

1137 rv += 0.5 

1138 return rv 

1139 

1140 

1141class XmlPhpLexer(DelegatingLexer): 

1142 """ 

1143 Subclass of `PhpLexer` that highlights unhandled data with the `XmlLexer`. 

1144 """ 

1145 

1146 name = 'XML+PHP' 

1147 aliases = ['xml+php'] 

1148 version_added = '' 

1149 alias_filenames = ['*.xml', '*.php', '*.php[345]'] 

1150 mimetypes = ['application/xml+php'] 

1151 url = 'https://www.php.net' 

1152 

1153 def __init__(self, **options): 

1154 super().__init__(XmlLexer, PhpLexer, **options) 

1155 

1156 def analyse_text(text): 

1157 rv = PhpLexer.analyse_text(text) - 0.01 

1158 if looks_like_xml(text): 

1159 rv += 0.4 

1160 return rv 

1161 

1162 

1163class CssPhpLexer(DelegatingLexer): 

1164 """ 

1165 Subclass of `PhpLexer` which highlights unmatched data with the `CssLexer`. 

1166 """ 

1167 

1168 name = 'CSS+PHP' 

1169 aliases = ['css+php'] 

1170 version_added = '' 

1171 alias_filenames = ['*.css'] 

1172 mimetypes = ['text/css+php'] 

1173 url = 'https://www.php.net' 

1174 

1175 def __init__(self, **options): 

1176 super().__init__(CssLexer, PhpLexer, **options) 

1177 

1178 def analyse_text(text): 

1179 return PhpLexer.analyse_text(text) - 0.05 

1180 

1181 

1182class JavascriptPhpLexer(DelegatingLexer): 

1183 """ 

1184 Subclass of `PhpLexer` which highlights unmatched data with the 

1185 `JavascriptLexer`. 

1186 """ 

1187 

1188 name = 'JavaScript+PHP' 

1189 aliases = ['javascript+php', 'js+php'] 

1190 version_added = '' 

1191 alias_filenames = ['*.js'] 

1192 mimetypes = ['application/x-javascript+php', 

1193 'text/x-javascript+php', 

1194 'text/javascript+php'] 

1195 url = 'https://www.php.net' 

1196 

1197 def __init__(self, **options): 

1198 super().__init__(JavascriptLexer, PhpLexer, **options) 

1199 

1200 def analyse_text(text): 

1201 return PhpLexer.analyse_text(text) 

1202 

1203 

1204class HtmlSmartyLexer(DelegatingLexer): 

1205 """ 

1206 Subclass of the `SmartyLexer` that highlights unlexed data with the 

1207 `HtmlLexer`. 

1208 

1209 Nested Javascript and CSS is highlighted too. 

1210 """ 

1211 

1212 name = 'HTML+Smarty' 

1213 aliases = ['html+smarty'] 

1214 version_added = '' 

1215 alias_filenames = ['*.html', '*.htm', '*.xhtml', '*.tpl'] 

1216 mimetypes = ['text/html+smarty'] 

1217 url = 'https://www.smarty.net/' 

1218 

1219 def __init__(self, **options): 

1220 super().__init__(HtmlLexer, SmartyLexer, **options) 

1221 

1222 def analyse_text(text): 

1223 rv = SmartyLexer.analyse_text(text) - 0.01 

1224 if html_doctype_matches(text): 

1225 rv += 0.5 

1226 return rv 

1227 

1228 

1229class XmlSmartyLexer(DelegatingLexer): 

1230 """ 

1231 Subclass of the `SmartyLexer` that highlights unlexed data with the 

1232 `XmlLexer`. 

1233 """ 

1234 

1235 name = 'XML+Smarty' 

1236 aliases = ['xml+smarty'] 

1237 version_added = '' 

1238 alias_filenames = ['*.xml', '*.tpl'] 

1239 mimetypes = ['application/xml+smarty'] 

1240 url = 'https://www.smarty.net/' 

1241 

1242 def __init__(self, **options): 

1243 super().__init__(XmlLexer, SmartyLexer, **options) 

1244 

1245 def analyse_text(text): 

1246 rv = SmartyLexer.analyse_text(text) - 0.01 

1247 if looks_like_xml(text): 

1248 rv += 0.4 

1249 return rv 

1250 

1251 

1252class CssSmartyLexer(DelegatingLexer): 

1253 """ 

1254 Subclass of the `SmartyLexer` that highlights unlexed data with the 

1255 `CssLexer`. 

1256 """ 

1257 

1258 name = 'CSS+Smarty' 

1259 aliases = ['css+smarty'] 

1260 version_added = '' 

1261 alias_filenames = ['*.css', '*.tpl'] 

1262 mimetypes = ['text/css+smarty'] 

1263 url = 'https://www.smarty.net/' 

1264 

1265 def __init__(self, **options): 

1266 super().__init__(CssLexer, SmartyLexer, **options) 

1267 

1268 def analyse_text(text): 

1269 return SmartyLexer.analyse_text(text) - 0.05 

1270 

1271 

1272class JavascriptSmartyLexer(DelegatingLexer): 

1273 """ 

1274 Subclass of the `SmartyLexer` that highlights unlexed data with the 

1275 `JavascriptLexer`. 

1276 """ 

1277 

1278 name = 'JavaScript+Smarty' 

1279 aliases = ['javascript+smarty', 'js+smarty'] 

1280 version_added = '' 

1281 alias_filenames = ['*.js', '*.tpl'] 

1282 mimetypes = ['application/x-javascript+smarty', 

1283 'text/x-javascript+smarty', 

1284 'text/javascript+smarty'] 

1285 url = 'https://www.smarty.net/' 

1286 

1287 def __init__(self, **options): 

1288 super().__init__(JavascriptLexer, SmartyLexer, **options) 

1289 

1290 def analyse_text(text): 

1291 return SmartyLexer.analyse_text(text) - 0.05 

1292 

1293 

1294class HtmlDjangoLexer(DelegatingLexer): 

1295 """ 

1296 Subclass of the `DjangoLexer` that highlights unlexed data with the 

1297 `HtmlLexer`. 

1298 

1299 Nested Javascript and CSS is highlighted too. 

1300 """ 

1301 

1302 name = 'HTML+Django/Jinja' 

1303 aliases = ['html+django', 'html+jinja', 'htmldjango'] 

1304 filenames = ['*.html.j2', '*.htm.j2', '*.xhtml.j2', '*.html.jinja2', '*.htm.jinja2', '*.xhtml.jinja2'] 

1305 version_added = '' 

1306 alias_filenames = ['*.html', '*.htm', '*.xhtml'] 

1307 mimetypes = ['text/html+django', 'text/html+jinja'] 

1308 url = 'https://www.djangoproject.com/documentation/templates' 

1309 

1310 def __init__(self, **options): 

1311 super().__init__(HtmlLexer, DjangoLexer, **options) 

1312 

1313 def analyse_text(text): 

1314 rv = DjangoLexer.analyse_text(text) - 0.01 

1315 if html_doctype_matches(text): 

1316 rv += 0.5 

1317 return rv 

1318 

1319 

1320class XmlDjangoLexer(DelegatingLexer): 

1321 """ 

1322 Subclass of the `DjangoLexer` that highlights unlexed data with the 

1323 `XmlLexer`. 

1324 """ 

1325 

1326 name = 'XML+Django/Jinja' 

1327 aliases = ['xml+django', 'xml+jinja'] 

1328 filenames = ['*.xml.j2', '*.xml.jinja2'] 

1329 version_added = '' 

1330 alias_filenames = ['*.xml'] 

1331 mimetypes = ['application/xml+django', 'application/xml+jinja'] 

1332 url = 'https://www.djangoproject.com/documentation/templates' 

1333 

1334 def __init__(self, **options): 

1335 super().__init__(XmlLexer, DjangoLexer, **options) 

1336 

1337 def analyse_text(text): 

1338 rv = DjangoLexer.analyse_text(text) - 0.01 

1339 if looks_like_xml(text): 

1340 rv += 0.4 

1341 return rv 

1342 

1343 

1344class CssDjangoLexer(DelegatingLexer): 

1345 """ 

1346 Subclass of the `DjangoLexer` that highlights unlexed data with the 

1347 `CssLexer`. 

1348 """ 

1349 

1350 name = 'CSS+Django/Jinja' 

1351 aliases = ['css+django', 'css+jinja'] 

1352 filenames = ['*.css.j2', '*.css.jinja2'] 

1353 version_added = '' 

1354 alias_filenames = ['*.css'] 

1355 mimetypes = ['text/css+django', 'text/css+jinja'] 

1356 url = 'https://www.djangoproject.com/documentation/templates' 

1357 

1358 def __init__(self, **options): 

1359 super().__init__(CssLexer, DjangoLexer, **options) 

1360 

1361 def analyse_text(text): 

1362 return DjangoLexer.analyse_text(text) - 0.05 

1363 

1364 

1365class JavascriptDjangoLexer(DelegatingLexer): 

1366 """ 

1367 Subclass of the `DjangoLexer` that highlights unlexed data with the 

1368 `JavascriptLexer`. 

1369 """ 

1370 

1371 name = 'JavaScript+Django/Jinja' 

1372 aliases = ['javascript+django', 'js+django', 

1373 'javascript+jinja', 'js+jinja'] 

1374 filenames = ['*.js.j2', '*.js.jinja2'] 

1375 version_added = '' 

1376 alias_filenames = ['*.js'] 

1377 mimetypes = ['application/x-javascript+django', 

1378 'application/x-javascript+jinja', 

1379 'text/x-javascript+django', 

1380 'text/x-javascript+jinja', 

1381 'text/javascript+django', 

1382 'text/javascript+jinja'] 

1383 url = 'https://www.djangoproject.com/documentation/templates' 

1384 

1385 def __init__(self, **options): 

1386 super().__init__(JavascriptLexer, DjangoLexer, **options) 

1387 

1388 def analyse_text(text): 

1389 return DjangoLexer.analyse_text(text) - 0.05 

1390 

1391 

1392class JspRootLexer(RegexLexer): 

1393 """ 

1394 Base for the `JspLexer`. Yields `Token.Other` for area outside of 

1395 JSP tags. 

1396 

1397 .. versionadded:: 0.7 

1398 """ 

1399 

1400 tokens = { 

1401 'root': [ 

1402 (r'<%\S?', Keyword, 'sec'), 

1403 # FIXME: I want to make these keywords but still parse attributes. 

1404 (r'</?jsp:(forward|getProperty|include|plugin|setProperty|useBean).*?>', 

1405 Keyword), 

1406 (r'[^<]+', Other), 

1407 (r'<', Other), 

1408 ], 

1409 'sec': [ 

1410 (r'%>', Keyword, '#pop'), 

1411 # note: '\w\W' != '.' without DOTALL. 

1412 (r'[\w\W]+?(?=%>|\Z)', using(JavaLexer)), 

1413 ], 

1414 } 

1415 

1416 

1417class JspLexer(DelegatingLexer): 

1418 """ 

1419 Lexer for Java Server Pages. 

1420 """ 

1421 name = 'Java Server Page' 

1422 aliases = ['jsp'] 

1423 filenames = ['*.jsp'] 

1424 mimetypes = ['application/x-jsp'] 

1425 url = 'https://projects.eclipse.org/projects/ee4j.jsp' 

1426 version_added = '0.7' 

1427 

1428 def __init__(self, **options): 

1429 super().__init__(XmlLexer, JspRootLexer, **options) 

1430 

1431 def analyse_text(text): 

1432 rv = JavaLexer.analyse_text(text) - 0.01 

1433 if looks_like_xml(text): 

1434 rv += 0.4 

1435 if '<%' in text and '%>' in text: 

1436 rv += 0.1 

1437 return rv 

1438 

1439 

1440class EvoqueLexer(RegexLexer): 

1441 """ 

1442 For files using the Evoque templating system. 

1443 """ 

1444 name = 'Evoque' 

1445 aliases = ['evoque'] 

1446 filenames = ['*.evoque'] 

1447 mimetypes = ['application/x-evoque'] 

1448 url = 'https://gizmojo.org/templating' 

1449 version_added = '1.1' 

1450 

1451 flags = re.DOTALL 

1452 

1453 tokens = { 

1454 'root': [ 

1455 (r'[^#$]+', Other), 

1456 (r'#\[', Comment.Multiline, 'comment'), 

1457 (r'\$\$', Other), 

1458 # svn keywords 

1459 (r'\$\w+:[^$\n]*\$', Comment.Multiline), 

1460 # directives: begin, end 

1461 (r'(\$)(begin|end)(\{(%)?)(.*?)((?(4)%)\})', 

1462 bygroups(Punctuation, Name.Builtin, Punctuation, None, 

1463 String, Punctuation)), 

1464 # directives: evoque, overlay 

1465 # see doc for handling first name arg: /directives/evoque/ 

1466 # + minor inconsistency: the "name" in e.g. $overlay{name=site_base} 

1467 # should be using(PythonLexer), not passed out as String 

1468 (r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+)?' 

1469 r'(.*?)((?(4)%)\})', 

1470 bygroups(Punctuation, Name.Builtin, Punctuation, None, 

1471 String, using(PythonLexer), Punctuation)), 

1472 # directives: if, for, prefer, test 

1473 (r'(\$)(\w+)(\{(%)?)(.*?)((?(4)%)\})', 

1474 bygroups(Punctuation, Name.Builtin, Punctuation, None, 

1475 using(PythonLexer), Punctuation)), 

1476 # directive clauses (no {} expression) 

1477 (r'(\$)(else|rof|fi)', bygroups(Punctuation, Name.Builtin)), 

1478 # expressions 

1479 (r'(\$\{(%)?)(.*?)((!)(.*?))?((?(2)%)\})', 

1480 bygroups(Punctuation, None, using(PythonLexer), 

1481 Name.Builtin, None, None, Punctuation)), 

1482 (r'#', Other), 

1483 ], 

1484 'comment': [ 

1485 (r'[^\]#]', Comment.Multiline), 

1486 (r'#\[', Comment.Multiline, '#push'), 

1487 (r'\]#', Comment.Multiline, '#pop'), 

1488 (r'[\]#]', Comment.Multiline) 

1489 ], 

1490 } 

1491 

1492 def analyse_text(text): 

1493 """Evoque templates use $evoque, which is unique.""" 

1494 if '$evoque' in text: 

1495 return 1 

1496 

1497class EvoqueHtmlLexer(DelegatingLexer): 

1498 """ 

1499 Subclass of the `EvoqueLexer` that highlights unlexed data with the 

1500 `HtmlLexer`. 

1501 """ 

1502 name = 'HTML+Evoque' 

1503 aliases = ['html+evoque'] 

1504 alias_filenames = ['*.html'] 

1505 mimetypes = ['text/html+evoque'] 

1506 url = 'https://gizmojo.org/templating' 

1507 version_added = '1.1' 

1508 

1509 def __init__(self, **options): 

1510 super().__init__(HtmlLexer, EvoqueLexer, **options) 

1511 

1512 def analyse_text(text): 

1513 return EvoqueLexer.analyse_text(text) 

1514 

1515 

1516class EvoqueXmlLexer(DelegatingLexer): 

1517 """ 

1518 Subclass of the `EvoqueLexer` that highlights unlexed data with the 

1519 `XmlLexer`. 

1520 """ 

1521 name = 'XML+Evoque' 

1522 aliases = ['xml+evoque'] 

1523 alias_filenames = ['*.xml'] 

1524 mimetypes = ['application/xml+evoque'] 

1525 url = 'https://gizmojo.org/templating' 

1526 version_added = '1.1' 

1527 

1528 def __init__(self, **options): 

1529 super().__init__(XmlLexer, EvoqueLexer, **options) 

1530 

1531 def analyse_text(text): 

1532 return EvoqueLexer.analyse_text(text) 

1533 

1534 

1535class ColdfusionLexer(RegexLexer): 

1536 """ 

1537 Coldfusion statements 

1538 """ 

1539 name = 'cfstatement' 

1540 aliases = ['cfs'] 

1541 filenames = [] 

1542 mimetypes = [] 

1543 url = 'https://www.adobe.com/products/coldfusion-family.html' 

1544 version_added = '' 

1545 

1546 flags = re.IGNORECASE 

1547 

1548 tokens = { 

1549 'root': [ 

1550 (r'//.*?\n', Comment.Single), 

1551 (r'/\*(?:.|\n)*?\*/', Comment.Multiline), 

1552 (r'\+\+|--', Operator), 

1553 (r'[-+*/^&=!]', Operator), 

1554 (r'<=|>=|<|>|==', Operator), 

1555 (r'mod\b', Operator), 

1556 (r'(eq|lt|gt|lte|gte|not|is|and|or)\b', Operator), 

1557 (r'\|\||&&', Operator), 

1558 (r'\?', Operator), 

1559 (r'"', String.Double, 'string'), 

1560 # There is a special rule for allowing html in single quoted 

1561 # strings, evidently. 

1562 (r"'.*?'", String.Single), 

1563 (r'\d+', Number), 

1564 (r'(if|else|len|var|xml|default|break|switch|component|property|function|do|' 

1565 r'try|catch|in|continue|for|return|while|required|any|array|binary|boolean|' 

1566 r'component|date|guid|numeric|query|string|struct|uuid|case)\b', Keyword), 

1567 (r'(true|false|null)\b', Keyword.Constant), 

1568 (r'(application|session|client|cookie|super|this|variables|arguments)\b', 

1569 Name.Constant), 

1570 (r'([a-z_$][\w.]*)(\s*)(\()', 

1571 bygroups(Name.Function, Text, Punctuation)), 

1572 (r'[a-z_$][\w.]*', Name.Variable), 

1573 (r'[()\[\]{};:,.\\]', Punctuation), 

1574 (r'\s+', Text), 

1575 ], 

1576 'string': [ 

1577 (r'""', String.Double), 

1578 (r'#.+?#', String.Interp), 

1579 (r'[^"#]+', String.Double), 

1580 (r'#', String.Double), 

1581 (r'"', String.Double, '#pop'), 

1582 ], 

1583 } 

1584 

1585 

1586class ColdfusionMarkupLexer(RegexLexer): 

1587 """ 

1588 Coldfusion markup only 

1589 """ 

1590 name = 'Coldfusion' 

1591 aliases = ['cf'] 

1592 filenames = [] 

1593 mimetypes = [] 

1594 url = 'https://www.adobe.com/products/coldfusion-family.html' 

1595 

1596 tokens = { 

1597 'root': [ 

1598 (r'[^<]+', Other), 

1599 include('tags'), 

1600 (r'<[^<>]*', Other), 

1601 ], 

1602 'tags': [ 

1603 (r'<!---', Comment.Multiline, 'cfcomment'), 

1604 (r'(?s)<!--.*?-->', Comment), 

1605 (r'<cfoutput.*?>', Name.Builtin, 'cfoutput'), 

1606 (r'(?s)(<cfscript.*?>)(.+?)(</cfscript.*?>)', 

1607 bygroups(Name.Builtin, using(ColdfusionLexer), Name.Builtin)), 

1608 # negative lookbehind is for strings with embedded > 

1609 (r'(?s)(</?cf(?:component|include|if|else|elseif|loop|return|' 

1610 r'dbinfo|dump|abort|location|invoke|throw|file|savecontent|' 

1611 r'mailpart|mail|header|content|zip|image|lock|argument|try|' 

1612 r'catch|break|directory|http|set|function|param)\b)(.*?)((?<!\\)>)', 

1613 bygroups(Name.Builtin, using(ColdfusionLexer), Name.Builtin)), 

1614 ], 

1615 'cfoutput': [ 

1616 (r'[^#<]+', Other), 

1617 (r'(#)(.*?)(#)', bygroups(Punctuation, using(ColdfusionLexer), 

1618 Punctuation)), 

1619 # (r'<cfoutput.*?>', Name.Builtin, '#push'), 

1620 (r'</cfoutput.*?>', Name.Builtin, '#pop'), 

1621 include('tags'), 

1622 (r'(?s)<[^<>]*', Other), 

1623 (r'#', Other), 

1624 ], 

1625 'cfcomment': [ 

1626 (r'<!---', Comment.Multiline, '#push'), 

1627 (r'--->', Comment.Multiline, '#pop'), 

1628 (r'([^<-]|<(?!!---)|-(?!-->))+', Comment.Multiline), 

1629 ], 

1630 } 

1631 

1632 

1633class ColdfusionHtmlLexer(DelegatingLexer): 

1634 """ 

1635 Coldfusion markup in html 

1636 """ 

1637 name = 'Coldfusion HTML' 

1638 aliases = ['cfm'] 

1639 filenames = ['*.cfm', '*.cfml'] 

1640 mimetypes = ['application/x-coldfusion'] 

1641 url = 'https://www.adobe.com/products/coldfusion-family.html' 

1642 version_added = '' 

1643 

1644 def __init__(self, **options): 

1645 super().__init__(HtmlLexer, ColdfusionMarkupLexer, **options) 

1646 

1647 

1648class ColdfusionCFCLexer(DelegatingLexer): 

1649 """ 

1650 Coldfusion markup/script components 

1651 """ 

1652 name = 'Coldfusion CFC' 

1653 aliases = ['cfc'] 

1654 filenames = ['*.cfc'] 

1655 mimetypes = [] 

1656 url = 'https://www.adobe.com/products/coldfusion-family.html' 

1657 version_added = '2.0' 

1658 

1659 def __init__(self, **options): 

1660 super().__init__(ColdfusionHtmlLexer, ColdfusionLexer, **options) 

1661 

1662 

1663class SspLexer(DelegatingLexer): 

1664 """ 

1665 Lexer for Scalate Server Pages. 

1666 """ 

1667 name = 'Scalate Server Page' 

1668 aliases = ['ssp'] 

1669 filenames = ['*.ssp'] 

1670 mimetypes = ['application/x-ssp'] 

1671 url = 'https://scalate.github.io/scalate/' 

1672 version_added = '1.4' 

1673 

1674 def __init__(self, **options): 

1675 super().__init__(XmlLexer, JspRootLexer, **options) 

1676 

1677 def analyse_text(text): 

1678 rv = 0.0 

1679 if re.search(r'val \w+\s*:', text): 

1680 rv += 0.6 

1681 if looks_like_xml(text): 

1682 rv += 0.2 

1683 if '<%' in text and '%>' in text: 

1684 rv += 0.1 

1685 return rv 

1686 

1687 

1688class TeaTemplateRootLexer(RegexLexer): 

1689 """ 

1690 Base for the `TeaTemplateLexer`. Yields `Token.Other` for area outside of 

1691 code blocks. 

1692 

1693 .. versionadded:: 1.5 

1694 """ 

1695 

1696 tokens = { 

1697 'root': [ 

1698 (r'<%\S?', Keyword, 'sec'), 

1699 (r'[^<]+', Other), 

1700 (r'<', Other), 

1701 ], 

1702 'sec': [ 

1703 (r'%>', Keyword, '#pop'), 

1704 # note: '\w\W' != '.' without DOTALL. 

1705 (r'[\w\W]+?(?=%>|\Z)', using(TeaLangLexer)), 

1706 ], 

1707 } 

1708 

1709 

1710class TeaTemplateLexer(DelegatingLexer): 

1711 """ 

1712 Lexer for Tea Templates. 

1713 """ 

1714 name = 'Tea' 

1715 aliases = ['tea'] 

1716 filenames = ['*.tea'] 

1717 mimetypes = ['text/x-tea'] 

1718 url = 'https://github.com/teatrove/teatrove' 

1719 version_added = '1.5' 

1720 

1721 def __init__(self, **options): 

1722 super().__init__(XmlLexer, TeaTemplateRootLexer, **options) 

1723 

1724 def analyse_text(text): 

1725 rv = TeaLangLexer.analyse_text(text) - 0.01 

1726 if looks_like_xml(text): 

1727 rv += 0.4 

1728 if '<%' in text and '%>' in text: 

1729 rv += 0.1 

1730 return rv 

1731 

1732 

1733class LassoHtmlLexer(DelegatingLexer): 

1734 """ 

1735 Subclass of the `LassoLexer` which highlights unhandled data with the 

1736 `HtmlLexer`. 

1737 

1738 Nested JavaScript and CSS is also highlighted. 

1739 """ 

1740 

1741 name = 'HTML+Lasso' 

1742 aliases = ['html+lasso'] 

1743 version_added = '1.6' 

1744 alias_filenames = ['*.html', '*.htm', '*.xhtml', '*.lasso', '*.lasso[89]', 

1745 '*.incl', '*.inc', '*.las'] 

1746 mimetypes = ['text/html+lasso', 

1747 'application/x-httpd-lasso', 

1748 'application/x-httpd-lasso[89]'] 

1749 url = 'https://www.lassosoft.com' 

1750 

1751 def __init__(self, **options): 

1752 super().__init__(HtmlLexer, LassoLexer, **options) 

1753 

1754 def analyse_text(text): 

1755 rv = LassoLexer.analyse_text(text) - 0.01 

1756 if html_doctype_matches(text): # same as HTML lexer 

1757 rv += 0.5 

1758 return rv 

1759 

1760 

1761class LassoXmlLexer(DelegatingLexer): 

1762 """ 

1763 Subclass of the `LassoLexer` which highlights unhandled data with the 

1764 `XmlLexer`. 

1765 """ 

1766 

1767 name = 'XML+Lasso' 

1768 aliases = ['xml+lasso'] 

1769 version_added = '1.6' 

1770 alias_filenames = ['*.xml', '*.lasso', '*.lasso[89]', 

1771 '*.incl', '*.inc', '*.las'] 

1772 mimetypes = ['application/xml+lasso'] 

1773 url = 'https://www.lassosoft.com' 

1774 

1775 def __init__(self, **options): 

1776 super().__init__(XmlLexer, LassoLexer, **options) 

1777 

1778 def analyse_text(text): 

1779 rv = LassoLexer.analyse_text(text) - 0.01 

1780 if looks_like_xml(text): 

1781 rv += 0.4 

1782 return rv 

1783 

1784 

1785class LassoCssLexer(DelegatingLexer): 

1786 """ 

1787 Subclass of the `LassoLexer` which highlights unhandled data with the 

1788 `CssLexer`. 

1789 """ 

1790 

1791 name = 'CSS+Lasso' 

1792 aliases = ['css+lasso'] 

1793 version_added = '1.6' 

1794 alias_filenames = ['*.css'] 

1795 mimetypes = ['text/css+lasso'] 

1796 url = 'https://www.lassosoft.com' 

1797 

1798 def __init__(self, **options): 

1799 options['requiredelimiters'] = True 

1800 super().__init__(CssLexer, LassoLexer, **options) 

1801 

1802 def analyse_text(text): 

1803 rv = LassoLexer.analyse_text(text) - 0.05 

1804 if re.search(r'\w+:[^;]+;', text): 

1805 rv += 0.1 

1806 if 'padding:' in text: 

1807 rv += 0.1 

1808 return rv 

1809 

1810 

1811class LassoJavascriptLexer(DelegatingLexer): 

1812 """ 

1813 Subclass of the `LassoLexer` which highlights unhandled data with the 

1814 `JavascriptLexer`. 

1815 """ 

1816 

1817 name = 'JavaScript+Lasso' 

1818 aliases = ['javascript+lasso', 'js+lasso'] 

1819 version_added = '1.6' 

1820 alias_filenames = ['*.js'] 

1821 mimetypes = ['application/x-javascript+lasso', 

1822 'text/x-javascript+lasso', 

1823 'text/javascript+lasso'] 

1824 url = 'https://www.lassosoft.com' 

1825 

1826 def __init__(self, **options): 

1827 options['requiredelimiters'] = True 

1828 super().__init__(JavascriptLexer, LassoLexer, **options) 

1829 

1830 def analyse_text(text): 

1831 rv = LassoLexer.analyse_text(text) - 0.05 

1832 return rv 

1833 

1834 

1835class HandlebarsLexer(RegexLexer): 

1836 """ 

1837 Generic handlebars template lexer. 

1838 

1839 Highlights only the Handlebars template tags (stuff between `{{` and `}}`). 

1840 Everything else is left for a delegating lexer. 

1841 """ 

1842 

1843 name = "Handlebars" 

1844 url = 'https://handlebarsjs.com/' 

1845 aliases = ['handlebars'] 

1846 version_added = '2.0' 

1847 

1848 tokens = { 

1849 'root': [ 

1850 (r'[^{]+', Other), 

1851 

1852 # Comment start {{! }} or {{!-- 

1853 (r'\{\{!.*\}\}', Comment), 

1854 

1855 # HTML Escaping open {{{expression 

1856 (r'(\{\{\{)(\s*)', bygroups(Comment.Special, Text), 'tag'), 

1857 

1858 # {{blockOpen {{#blockOpen {{/blockClose with optional tilde ~ 

1859 (r'(\{\{)([#~/]+)([^\s}]*)', 

1860 bygroups(Comment.Preproc, Number.Attribute, Number.Attribute), 'tag'), 

1861 (r'(\{\{)(\s*)', bygroups(Comment.Preproc, Text), 'tag'), 

1862 ], 

1863 

1864 'tag': [ 

1865 (r'\s+', Text), 

1866 # HTML Escaping close }}} 

1867 (r'\}\}\}', Comment.Special, '#pop'), 

1868 # blockClose}}, includes optional tilde ~ 

1869 (r'(~?)(\}\})', bygroups(Number, Comment.Preproc), '#pop'), 

1870 

1871 # {{opt=something}} 

1872 (r'([^\s}]+)(=)', bygroups(Name.Attribute, Operator)), 

1873 

1874 # Partials {{> ...}} 

1875 (r'(>)(\s*)(@partial-block)', bygroups(Keyword, Text, Keyword)), 

1876 (r'(#?>)(\s*)([\w-]+)', bygroups(Keyword, Text, Name.Variable)), 

1877 (r'(>)(\s*)(\()', bygroups(Keyword, Text, Punctuation), 

1878 'dynamic-partial'), 

1879 

1880 include('generic'), 

1881 ], 

1882 'dynamic-partial': [ 

1883 (r'\s+', Text), 

1884 (r'\)', Punctuation, '#pop'), 

1885 

1886 (r'(lookup)(\s+)(\.|this)(\s+)', bygroups(Keyword, Text, 

1887 Name.Variable, Text)), 

1888 (r'(lookup)(\s+)(\S+)', bygroups(Keyword, Text, 

1889 using(this, state='variable'))), 

1890 (r'[\w-]+', Name.Function), 

1891 

1892 include('generic'), 

1893 ], 

1894 'variable': [ 

1895 (r'[()/@a-zA-Z][\w-]*', Name.Variable), 

1896 (r'\.[\w-]+', Name.Variable), 

1897 (r'(this\/|\.\/|(\.\.\/)+)[\w-]+', Name.Variable), 

1898 ], 

1899 'generic': [ 

1900 include('variable'), 

1901 

1902 # borrowed from DjangoLexer 

1903 (r':?"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

1904 (r":?'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

1905 (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|" 

1906 r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

1907 ] 

1908 } 

1909 

1910 

1911class HandlebarsHtmlLexer(DelegatingLexer): 

1912 """ 

1913 Subclass of the `HandlebarsLexer` that highlights unlexed data with the 

1914 `HtmlLexer`. 

1915 """ 

1916 

1917 name = "HTML+Handlebars" 

1918 aliases = ["html+handlebars"] 

1919 filenames = ['*.handlebars', '*.hbs'] 

1920 mimetypes = ['text/html+handlebars', 'text/x-handlebars-template'] 

1921 url = 'https://handlebarsjs.com/' 

1922 version_added = '2.0' 

1923 

1924 def __init__(self, **options): 

1925 super().__init__(HtmlLexer, HandlebarsLexer, **options) 

1926 

1927 

1928class YamlJinjaLexer(DelegatingLexer): 

1929 """ 

1930 Subclass of the `DjangoLexer` that highlights unlexed data with the 

1931 `YamlLexer`. 

1932 

1933 Commonly used in Saltstack salt states. 

1934 """ 

1935 

1936 name = 'YAML+Jinja' 

1937 aliases = ['yaml+jinja', 'salt', 'sls'] 

1938 filenames = ['*.sls', '*.yaml.j2', '*.yml.j2', '*.yaml.jinja2', '*.yml.jinja2'] 

1939 mimetypes = ['text/x-yaml+jinja', 'text/x-sls'] 

1940 url = 'https://jinja.palletsprojects.com' 

1941 version_added = '2.0' 

1942 

1943 def __init__(self, **options): 

1944 super().__init__(YamlLexer, DjangoLexer, **options) 

1945 

1946 

1947class LiquidLexer(RegexLexer): 

1948 """ 

1949 Lexer for Liquid templates. 

1950 """ 

1951 name = 'liquid' 

1952 url = 'https://www.rubydoc.info/github/Shopify/liquid' 

1953 aliases = ['liquid'] 

1954 filenames = ['*.liquid'] 

1955 version_added = '2.0' 

1956 

1957 tokens = { 

1958 'root': [ 

1959 (r'[^{]+', Text), 

1960 # tags and block tags 

1961 (r'(\{%)(\s*)', bygroups(Punctuation, Whitespace), 'tag-or-block'), 

1962 # output tags 

1963 (r'(\{\{)(\s*)([^\s}]+)', 

1964 bygroups(Punctuation, Whitespace, using(this, state = 'generic')), 

1965 'output'), 

1966 (r'\{', Text) 

1967 ], 

1968 

1969 'tag-or-block': [ 

1970 # builtin logic blocks 

1971 (r'(if|unless|elsif|case)(?=\s+)', Keyword.Reserved, 'condition'), 

1972 (r'(when)(\s+)', bygroups(Keyword.Reserved, Whitespace), 

1973 combined('end-of-block', 'whitespace', 'generic')), 

1974 (r'(else)(\s*)(%\})', 

1975 bygroups(Keyword.Reserved, Whitespace, Punctuation), '#pop'), 

1976 

1977 # other builtin blocks 

1978 (r'(capture)(\s+)([^\s%]+)(\s*)(%\})', 

1979 bygroups(Name.Tag, Whitespace, using(this, state = 'variable'), 

1980 Whitespace, Punctuation), '#pop'), 

1981 (r'(comment)(\s*)(%\})', 

1982 bygroups(Name.Tag, Whitespace, Punctuation), 'comment'), 

1983 (r'(raw)(\s*)(%\})', 

1984 bygroups(Name.Tag, Whitespace, Punctuation), 'raw'), 

1985 

1986 # end of block 

1987 (r'(end(case|unless|if))(\s*)(%\})', 

1988 bygroups(Keyword.Reserved, None, Whitespace, Punctuation), '#pop'), 

1989 (r'(end([^\s%]+))(\s*)(%\})', 

1990 bygroups(Name.Tag, None, Whitespace, Punctuation), '#pop'), 

1991 

1992 # builtin tags (assign and include are handled together with usual tags) 

1993 (r'(cycle)(\s+)(?:([^\s:]*)(:))?(\s*)', 

1994 bygroups(Name.Tag, Whitespace, 

1995 using(this, state='generic'), Punctuation, Whitespace), 

1996 'variable-tag-markup'), 

1997 

1998 # other tags or blocks 

1999 (r'([^\s%]+)(\s*)', bygroups(Name.Tag, Whitespace), 'tag-markup') 

2000 ], 

2001 

2002 'output': [ 

2003 include('whitespace'), 

2004 (r'\}\}', Punctuation, '#pop'), # end of output 

2005 

2006 (r'\|', Punctuation, 'filters') 

2007 ], 

2008 

2009 'filters': [ 

2010 include('whitespace'), 

2011 (r'\}\}', Punctuation, ('#pop', '#pop')), # end of filters and output 

2012 

2013 (r'([^\s|:]+)(:?)(\s*)', 

2014 bygroups(Name.Function, Punctuation, Whitespace), 'filter-markup') 

2015 ], 

2016 

2017 'filter-markup': [ 

2018 (r'\|', Punctuation, '#pop'), 

2019 include('end-of-tag'), 

2020 include('default-param-markup') 

2021 ], 

2022 

2023 'condition': [ 

2024 include('end-of-block'), 

2025 include('whitespace'), 

2026 

2027 (r'([^\s=!><]+)(\s*)([=!><]=?)(\s*)(\S+)(\s*)(%\})', 

2028 bygroups(using(this, state = 'generic'), Whitespace, Operator, 

2029 Whitespace, using(this, state = 'generic'), Whitespace, 

2030 Punctuation)), 

2031 (r'\b!', Operator), 

2032 (r'\bnot\b', Operator.Word), 

2033 (r'([\w.\'"]+)(\s+)(contains)(\s+)([\w.\'"]+)', 

2034 bygroups(using(this, state = 'generic'), Whitespace, Operator.Word, 

2035 Whitespace, using(this, state = 'generic'))), 

2036 

2037 include('generic'), 

2038 include('whitespace') 

2039 ], 

2040 

2041 'generic-value': [ 

2042 include('generic'), 

2043 include('end-at-whitespace') 

2044 ], 

2045 

2046 'operator': [ 

2047 (r'(\s*)((=|!|>|<)=?)(\s*)', 

2048 bygroups(Whitespace, Operator, None, Whitespace), '#pop'), 

2049 (r'(\s*)(\bcontains\b)(\s*)', 

2050 bygroups(Whitespace, Operator.Word, Whitespace), '#pop'), 

2051 ], 

2052 

2053 'end-of-tag': [ 

2054 (r'\}\}', Punctuation, '#pop') 

2055 ], 

2056 

2057 'end-of-block': [ 

2058 (r'%\}', Punctuation, ('#pop', '#pop')) 

2059 ], 

2060 

2061 'end-at-whitespace': [ 

2062 (r'\s+', Whitespace, '#pop') 

2063 ], 

2064 

2065 # states for unknown markup 

2066 'param-markup': [ 

2067 include('whitespace'), 

2068 # params with colons or equals 

2069 (r'([^\s=:]+)(\s*)(=|:)', 

2070 bygroups(Name.Attribute, Whitespace, Operator)), 

2071 # explicit variables 

2072 (r'(\{\{)(\s*)([^\s}])(\s*)(\}\})', 

2073 bygroups(Punctuation, Whitespace, using(this, state = 'variable'), 

2074 Whitespace, Punctuation)), 

2075 

2076 include('string'), 

2077 include('number'), 

2078 include('keyword'), 

2079 (r',', Punctuation) 

2080 ], 

2081 

2082 'default-param-markup': [ 

2083 include('param-markup'), 

2084 (r'.', Text) # fallback for switches / variables / un-quoted strings / ... 

2085 ], 

2086 

2087 'variable-param-markup': [ 

2088 include('param-markup'), 

2089 include('variable'), 

2090 (r'.', Text) # fallback 

2091 ], 

2092 

2093 'tag-markup': [ 

2094 (r'%\}', Punctuation, ('#pop', '#pop')), # end of tag 

2095 include('default-param-markup') 

2096 ], 

2097 

2098 'variable-tag-markup': [ 

2099 (r'%\}', Punctuation, ('#pop', '#pop')), # end of tag 

2100 include('variable-param-markup') 

2101 ], 

2102 

2103 # states for different values types 

2104 'keyword': [ 

2105 (r'\b(false|true)\b', Keyword.Constant) 

2106 ], 

2107 

2108 'variable': [ 

2109 (r'[a-zA-Z_]\w*', Name.Variable), 

2110 (r'(?<=\w)\.(?=\w)', Punctuation) 

2111 ], 

2112 

2113 'string': [ 

2114 (r"'[^']*'", String.Single), 

2115 (r'"[^"]*"', String.Double) 

2116 ], 

2117 

2118 'number': [ 

2119 (r'\d+\.\d+', Number.Float), 

2120 (r'\d+', Number.Integer) 

2121 ], 

2122 

2123 'generic': [ # decides for variable, string, keyword or number 

2124 include('keyword'), 

2125 include('string'), 

2126 include('number'), 

2127 include('variable') 

2128 ], 

2129 

2130 'whitespace': [ 

2131 (r'[ \t]+', Whitespace) 

2132 ], 

2133 

2134 # states for builtin blocks 

2135 'comment': [ 

2136 (r'(\{%)(\s*)(endcomment)(\s*)(%\})', 

2137 bygroups(Punctuation, Whitespace, Name.Tag, Whitespace, 

2138 Punctuation), ('#pop', '#pop')), 

2139 (r'.', Comment) 

2140 ], 

2141 

2142 'raw': [ 

2143 (r'[^{]+', Text), 

2144 (r'(\{%)(\s*)(endraw)(\s*)(%\})', 

2145 bygroups(Punctuation, Whitespace, Name.Tag, Whitespace, 

2146 Punctuation), '#pop'), 

2147 (r'\{', Text) 

2148 ], 

2149 } 

2150 

2151 

2152class TwigLexer(RegexLexer): 

2153 """ 

2154 Twig template lexer. 

2155 

2156 It just highlights Twig code between the preprocessor directives, 

2157 other data is left untouched by the lexer. 

2158 """ 

2159 

2160 name = 'Twig' 

2161 aliases = ['twig'] 

2162 mimetypes = ['application/x-twig'] 

2163 url = 'https://twig.symfony.com' 

2164 version_added = '2.0' 

2165 

2166 flags = re.M | re.S 

2167 

2168 # Note that a backslash is included in the following two patterns 

2169 # PHP uses a backslash as a namespace separator 

2170 _ident_char = r'[\\\w-]|[^\x00-\x7f]' 

2171 _ident_begin = r'(?:[\\_a-z]|[^\x00-\x7f])' 

2172 _ident_end = r'(?:' + _ident_char + ')*' 

2173 _ident_inner = _ident_begin + _ident_end 

2174 

2175 tokens = { 

2176 'root': [ 

2177 (r'[^{]+', Other), 

2178 (r'\{\{', Comment.Preproc, 'var'), 

2179 # twig comments 

2180 (r'\{\#.*?\#\}', Comment), 

2181 # raw twig blocks 

2182 (r'(\{%)(-?\s*)(raw)(\s*-?)(%\})(.*?)' 

2183 r'(\{%)(-?\s*)(endraw)(\s*-?)(%\})', 

2184 bygroups(Comment.Preproc, Text, Keyword, Text, Comment.Preproc, 

2185 Other, Comment.Preproc, Text, Keyword, Text, 

2186 Comment.Preproc)), 

2187 (r'(\{%)(-?\s*)(verbatim)(\s*-?)(%\})(.*?)' 

2188 r'(\{%)(-?\s*)(endverbatim)(\s*-?)(%\})', 

2189 bygroups(Comment.Preproc, Text, Keyword, Text, Comment.Preproc, 

2190 Other, Comment.Preproc, Text, Keyword, Text, 

2191 Comment.Preproc)), 

2192 # filter blocks 

2193 (rf'(\{{%)(-?\s*)(filter)(\s+)({_ident_inner})', 

2194 bygroups(Comment.Preproc, Text, Keyword, Text, Name.Function), 

2195 'tag'), 

2196 (r'(\{%)(-?\s*)([a-zA-Z_]\w*)', 

2197 bygroups(Comment.Preproc, Text, Keyword), 'tag'), 

2198 (r'\{', Other), 

2199 ], 

2200 'varnames': [ 

2201 (rf'(\|)(\s*)({_ident_inner})', 

2202 bygroups(Operator, Text, Name.Function)), 

2203 (rf'(is)(\s+)(not)?(\s*)({_ident_inner})', 

2204 bygroups(Keyword, Text, Keyword, Text, Name.Function)), 

2205 (r'(?i)(true|false|none|null)\b', Keyword.Pseudo), 

2206 (r'(in|not|and|b-and|or|b-or|b-xor|is' 

2207 r'if|elseif|else|import' 

2208 r'constant|defined|divisibleby|empty|even|iterable|odd|sameas' 

2209 r'matches|starts\s+with|ends\s+with)\b', 

2210 Keyword), 

2211 (r'(loop|block|parent)\b', Name.Builtin), 

2212 (_ident_inner, Name.Variable), 

2213 (r'\.' + _ident_inner, Name.Variable), 

2214 (r'\.[0-9]+', Number), 

2215 (r':?"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

2216 (r":?'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

2217 (r'([{}()\[\]+\-*/,:~%]|\.\.|\?|:|\*\*|\/\/|!=|[><=]=?)', Operator), 

2218 (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|" 

2219 r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

2220 ], 

2221 'var': [ 

2222 (r'\s+', Text), 

2223 (r'(-?)(\}\})', bygroups(Text, Comment.Preproc), '#pop'), 

2224 include('varnames') 

2225 ], 

2226 'tag': [ 

2227 (r'\s+', Text), 

2228 (r'(-?)(%\})', bygroups(Text, Comment.Preproc), '#pop'), 

2229 include('varnames'), 

2230 (r'.', Punctuation), 

2231 ], 

2232 } 

2233 

2234 

2235class TwigHtmlLexer(DelegatingLexer): 

2236 """ 

2237 Subclass of the `TwigLexer` that highlights unlexed data with the 

2238 `HtmlLexer`. 

2239 """ 

2240 

2241 name = "HTML+Twig" 

2242 aliases = ["html+twig"] 

2243 filenames = ['*.twig'] 

2244 mimetypes = ['text/html+twig'] 

2245 url = 'https://twig.symfony.com' 

2246 version_added = '2.0' 

2247 

2248 def __init__(self, **options): 

2249 super().__init__(HtmlLexer, TwigLexer, **options) 

2250 

2251 

2252class Angular2Lexer(RegexLexer): 

2253 """ 

2254 Generic angular2 template lexer. 

2255 

2256 Highlights only the Angular template tags (stuff between `{{` and `}}` and 

2257 special attributes: '(event)=', '[property]=', '[(twoWayBinding)]='). 

2258 Everything else is left for a delegating lexer. 

2259 """ 

2260 

2261 name = "Angular2" 

2262 url = 'https://angular.io/guide/template-syntax' 

2263 aliases = ['ng2'] 

2264 version_added = '2.1' 

2265 

2266 tokens = { 

2267 'root': [ 

2268 (r'[^{([*#]+', Other), 

2269 

2270 # {{meal.name}} 

2271 (r'(\{\{)(\s*)', bygroups(Comment.Preproc, Text), 'ngExpression'), 

2272 

2273 # (click)="deleteOrder()"; [value]="test"; [(twoWayTest)]="foo.bar" 

2274 (r'([([]+)([\w:.-]+)([\])]+)(\s*)(=)(\s*)', 

2275 bygroups(Punctuation, Name.Attribute, Punctuation, Text, Operator, Text), 

2276 'attr'), 

2277 (r'([([]+)([\w:.-]+)([\])]+)(\s*)', 

2278 bygroups(Punctuation, Name.Attribute, Punctuation, Text)), 

2279 

2280 # *ngIf="..."; #f="ngForm" 

2281 (r'([*#])([\w:.-]+)(\s*)(=)(\s*)', 

2282 bygroups(Punctuation, Name.Attribute, Text, Operator, Text), 'attr'), 

2283 (r'([*#])([\w:.-]+)(\s*)', 

2284 bygroups(Punctuation, Name.Attribute, Text)), 

2285 ], 

2286 

2287 'ngExpression': [ 

2288 (r'\s+(\|\s+)?', Text), 

2289 (r'\}\}', Comment.Preproc, '#pop'), 

2290 

2291 # Literals 

2292 (r':?(true|false)', String.Boolean), 

2293 (r':?"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

2294 (r":?'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

2295 (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|" 

2296 r"0[xX][0-9a-fA-F]+[Ll]?", Number), 

2297 

2298 # Variabletext 

2299 (r'[a-zA-Z][\w-]*(\(.*\))?', Name.Variable), 

2300 (r'\.[\w-]+(\(.*\))?', Name.Variable), 

2301 

2302 # inline If 

2303 (r'(\?)(\s*)([^}\s]+)(\s*)(:)(\s*)([^}\s]+)(\s*)', 

2304 bygroups(Operator, Text, String, Text, Operator, Text, String, Text)), 

2305 ], 

2306 'attr': [ 

2307 ('".*?"', String, '#pop'), 

2308 ("'.*?'", String, '#pop'), 

2309 (r'[^\s>]+', String, '#pop'), 

2310 ], 

2311 } 

2312 

2313 

2314class Angular2HtmlLexer(DelegatingLexer): 

2315 """ 

2316 Subclass of the `Angular2Lexer` that highlights unlexed data with the 

2317 `HtmlLexer`. 

2318 """ 

2319 

2320 name = "HTML + Angular2" 

2321 aliases = ["html+ng2"] 

2322 filenames = ['*.ng2'] 

2323 url = 'https://angular.io/guide/template-syntax' 

2324 version_added = '2.0' 

2325 

2326 def __init__(self, **options): 

2327 super().__init__(HtmlLexer, Angular2Lexer, **options) 

2328 

2329 

2330class SqlJinjaLexer(DelegatingLexer): 

2331 """ 

2332 Templated SQL lexer. 

2333 """ 

2334 

2335 name = 'SQL+Jinja' 

2336 aliases = ['sql+jinja'] 

2337 filenames = ['*.sql', '*.sql.j2', '*.sql.jinja2'] 

2338 url = 'https://jinja.palletsprojects.com' 

2339 version_added = '2.13' 

2340 

2341 def __init__(self, **options): 

2342 super().__init__(SqlLexer, DjangoLexer, **options) 

2343 

2344 def analyse_text(text): 

2345 rv = 0.0 

2346 # dbt's ref function 

2347 if re.search(r'\{\{\s*ref\(.*\)\s*\}\}', text): 

2348 rv += 0.4 

2349 # dbt's source function 

2350 if re.search(r'\{\{\s*source\(.*\)\s*\}\}', text): 

2351 rv += 0.25 

2352 # Jinja macro 

2353 if re.search(r'\{%-?\s*macro \w+\(.*\)\s*-?%\}', text): 

2354 rv += 0.15 

2355 return rv