codekingpro/portable-devtools
114k
1"""2 pygments.lexers.configs3 ~~~~~~~~~~~~~~~~~~~~~~~4 5 Lexers for configuration file formats.6 7 :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.8 :license: BSD, see LICENSE for details.9"""10 11import re12 13from pygments.lexer import ExtendedRegexLexer, RegexLexer, default, words, \14 bygroups, include, using, line_re15from pygments.token import Text, Comment, Operator, Keyword, Name, String, \16 Number, Punctuation, Whitespace, Literal, Error, Generic17from pygments.lexers.shell import BashLexer18from pygments.lexers.data import JsonLexer19 20__all__ = ['IniLexer', 'SystemdLexer', 'DesktopLexer', 'RegeditLexer', 'PropertiesLexer',21 'KconfigLexer', 'Cfengine3Lexer', 'ApacheConfLexer', 'SquidConfLexer',22 'NginxConfLexer', 'LighttpdConfLexer', 'DockerLexer',23 'TerraformLexer', 'TermcapLexer', 'TerminfoLexer',24 'PkgConfigLexer', 'PacmanConfLexer', 'AugeasLexer', 'TOMLLexer',25 'NestedTextLexer', 'SingularityLexer', 'UnixConfigLexer']26 27 28class IniLexer(RegexLexer):29 """30 Lexer for configuration files in INI style.31 """32 33 name = 'INI'34 aliases = ['ini', 'cfg', 'dosini']35 filenames = [36 '*.ini', '*.cfg', '*.inf', '.editorconfig',37 ]38 mimetypes = ['text/x-ini', 'text/inf']39 url = 'https://en.wikipedia.org/wiki/INI_file'40 version_added = ''41 42 tokens = {43 'root': [44 (r'\s+', Whitespace),45 (r'[;#].*', Comment.Single),46 (r'(\[.*?\])([ \t]*)$', bygroups(Keyword, Whitespace)),47 (r'''(.*?)([ \t]*)([=:])([ \t]*)(["'])''',48 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String),49 "quoted_value"),50 (r'(.*?)([ \t]*)([=:])([ \t]*)([^;#\n]*)(\\)(\s+)',51 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String,52 Text, Whitespace),53 "value"),54 (r'(.*?)([ \t]*)([=:])([ \t]*)([^ ;#\n]*(?: +[^ ;#\n]+)*)',55 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String)),56 # standalone option, supported by some INI parsers57 (r'(.+?)$', Name.Attribute),58 ],59 'quoted_value': [60 (r'''([^"'\n]*)(["'])(\s*)''',61 bygroups(String, String, Whitespace), "#pop"),62 (r'[;#].*', Comment.Single),63 (r'$', String, "#pop"),64 ],65 'value': [ # line continuation66 (r'\s+', Whitespace),67 (r'(\s*)(.*)(\\)([ \t]*)',68 bygroups(Whitespace, String, Text, Whitespace)),69 (r'.*$', String, "#pop"),70 ],71 }72 73 def analyse_text(text):74 npos = text.find('\n')75 if npos < 3:76 return False77 if text[0] == '[' and text[npos-1] == ']':78 return 0.879 return False80 81 82class DesktopLexer(RegexLexer):83 """84 Lexer for .desktop files.85 """86 87 name = 'Desktop file'88 url = "https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html"89 aliases = ['desktop']90 filenames = ['*.desktop']91 mimetypes = ['application/x-desktop']92 version_added = '2.16'93 94 tokens = {95 'root': [96 (r'^[ \t]*\n', Whitespace),97 (r'^(#.*)(\n)', bygroups(Comment.Single, Whitespace)),98 (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)),99 (r'([-A-Za-z0-9]+)(\[[^\] \t=]+\])?([ \t]*)(=)([ \t]*)([^\n]*)([ \t\n]*\n)',100 bygroups(Name.Attribute, Name.Namespace, Whitespace, Operator, Whitespace, String, Whitespace)),101 ],102 }103 104 def analyse_text(text):105 if text.startswith("[Desktop Entry]"):106 return 1.0107 if re.search(r"^\[Desktop Entry\][ \t]*$", text[:500], re.MULTILINE) is not None:108 return 0.9109 return 0.0110 111 112class SystemdLexer(RegexLexer):113 """114 Lexer for systemd unit files.115 """116 117 name = 'Systemd'118 url = "https://www.freedesktop.org/software/systemd/man/systemd.syntax.html"119 aliases = ['systemd']120 filenames = [121 '*.service', '*.socket', '*.device', '*.mount', '*.automount',122 '*.swap', '*.target', '*.path', '*.timer', '*.slice', '*.scope',123 ]124 version_added = '2.16'125 126 tokens = {127 'root': [128 (r'^[ \t]*\n', Whitespace),129 (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)),130 (r'(\[[^\]\n]+\])(\n)', bygroups(Keyword, Whitespace)),131 (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\\)(\n)',132 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String,133 Text, Whitespace),134 "value"),135 (r'([^=]+)([ \t]*)(=)([ \t]*)([^\n]*)(\n)',136 bygroups(Name.Attribute, Whitespace, Operator, Whitespace, String, Whitespace)),137 ],138 'value': [139 # line continuation140 (r'^([;#].*)(\n)', bygroups(Comment.Single, Whitespace)),141 (r'([ \t]*)([^\n]*)(\\)(\n)',142 bygroups(Whitespace, String, Text, Whitespace)),143 (r'([ \t]*)([^\n]*)(\n)',144 bygroups(Whitespace, String, Whitespace), "#pop"),145 ],146 }147 148 def analyse_text(text):149 if text.startswith("[Unit]"):150 return 1.0151 if re.search(r"^\[Unit\][ \t]*$", text[:500], re.MULTILINE) is not None:152 return 0.9153 return 0.0154 155 156class RegeditLexer(RegexLexer):157 """158 Lexer for Windows Registry files produced by regedit.159 """160 161 name = 'reg'162 url = 'http://en.wikipedia.org/wiki/Windows_Registry#.REG_files'163 aliases = ['registry']164 filenames = ['*.reg']165 mimetypes = ['text/x-windows-registry']166 version_added = '1.6'167 168 tokens = {169 'root': [170 (r'Windows Registry Editor.*', Text),171 (r'\s+', Whitespace),172 (r'[;#].*', Comment.Single),173 (r'(\[)(-?)(HKEY_[A-Z_]+)(.*?\])$',174 bygroups(Keyword, Operator, Name.Builtin, Keyword)),175 # String keys, which obey somewhat normal escaping176 (r'("(?:\\"|\\\\|[^"])+")([ \t]*)(=)([ \t]*)',177 bygroups(Name.Attribute, Whitespace, Operator, Whitespace),178 'value'),179 # Bare keys (includes @)180 (r'(.*?)([ \t]*)(=)([ \t]*)',181 bygroups(Name.Attribute, Whitespace, Operator, Whitespace),182 'value'),183 ],184 'value': [185 (r'-', Operator, '#pop'), # delete value186 (r'(dword|hex(?:\([0-9a-fA-F]\))?)(:)([0-9a-fA-F,]+)',187 bygroups(Name.Variable, Punctuation, Number), '#pop'),188 # As far as I know, .reg files do not support line continuation.189 (r'.+', String, '#pop'),190 default('#pop'),191 ]192 }193 194 def analyse_text(text):195 return text.startswith('Windows Registry Editor')196 197 198class PropertiesLexer(RegexLexer):199 """200 Lexer for configuration files in Java's properties format.201 202 Note: trailing whitespace counts as part of the value as per spec203 """204 205 name = 'Properties'206 aliases = ['properties', 'jproperties']207 filenames = ['*.properties']208 mimetypes = ['text/x-java-properties']209 url = 'https://en.wikipedia.org/wiki/.properties'210 version_added = '1.4'211 212 tokens = {213 'root': [214 # comments215 (r'[!#].*|/{2}.*', Comment.Single),216 # ending a comment or whitespace-only line217 (r'\n', Whitespace),218 # eat whitespace at the beginning of a line219 (r'^[^\S\n]+', Whitespace),220 # start lexing a key221 default('key'),222 ],223 'key': [224 # non-escaped key characters225 (r'[^\\:=\s]+', Name.Attribute),226 # escapes227 include('escapes'),228 # separator is the first non-escaped whitespace or colon or '=' on the line;229 # if it's whitespace, = and : are gobbled after it230 (r'([^\S\n]*)([:=])([^\S\n]*)',231 bygroups(Whitespace, Operator, Whitespace),232 ('#pop', 'value')),233 (r'[^\S\n]+', Whitespace, ('#pop', 'value')),234 # maybe we got no value after all235 (r'\n', Whitespace, '#pop'),236 ],237 'value': [238 # non-escaped value characters239 (r'[^\\\n]+', String),240 # escapes241 include('escapes'),242 # end the value on an unescaped newline243 (r'\n', Whitespace, '#pop'),244 ],245 'escapes': [246 # line continuations; these gobble whitespace at the beginning of the next line247 (r'(\\\n)([^\S\n]*)', bygroups(String.Escape, Whitespace)),248 # other escapes249 (r'\\(.|\n)', String.Escape),250 ],251 }252 253 254def _rx_indent(level):255 # Kconfig *always* interprets a tab as 8 spaces, so this is the default.256 # Edit this if you are in an environment where KconfigLexer gets expanded257 # input (tabs expanded to spaces) and the expansion tab width is != 8,258 # e.g. in connection with Trac (trac.ini, [mimeviewer], tab_width).259 # Value range here is 2 <= {tab_width} <= 8.260 tab_width = 8261 # Regex matching a given indentation {level}, assuming that indentation is262 # a multiple of {tab_width}. In other cases there might be problems.263 if tab_width == 2:264 space_repeat = '+'265 else:266 space_repeat = '{1,%d}' % (tab_width - 1)267 if level == 1:268 level_repeat = ''269 else:270 level_repeat = f'{{{level}}}'271 return rf'(?:\t| {space_repeat}\t| {{{tab_width}}}){level_repeat}.*\n'272 273 274class KconfigLexer(RegexLexer):275 """276 For Linux-style Kconfig files.277 """278 279 name = 'Kconfig'280 aliases = ['kconfig', 'menuconfig', 'linux-config', 'kernel-config']281 version_added = '1.6'282 # Adjust this if new kconfig file names appear in your environment283 filenames = ['Kconfig*', '*Config.in*', 'external.in*',284 'standard-modules.in']285 mimetypes = ['text/x-kconfig']286 url = 'https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html'287 288 # No re.MULTILINE, indentation-aware help text needs line-by-line handling289 flags = 0290 291 def call_indent(level):292 # If indentation >= {level} is detected, enter state 'indent{level}'293 return (_rx_indent(level), String.Doc, f'indent{level}')294 295 def do_indent(level):296 # Print paragraphs of indentation level >= {level} as String.Doc,297 # ignoring blank lines. Then return to 'root' state.298 return [299 (_rx_indent(level), String.Doc),300 (r'\s*\n', Text),301 default('#pop:2')302 ]303 304 tokens = {305 'root': [306 (r'\s+', Whitespace),307 (r'#.*?\n', Comment.Single),308 (words((309 'mainmenu', 'config', 'menuconfig', 'choice', 'endchoice',310 'comment', 'menu', 'endmenu', 'visible if', 'if', 'endif',311 'source', 'prompt', 'select', 'depends on', 'default',312 'range', 'option'), suffix=r'\b'),313 Keyword),314 (r'(---help---|help)[\t ]*\n', Keyword, 'help'),315 (r'(bool|tristate|string|hex|int|defconfig_list|modules|env)\b',316 Name.Builtin),317 (r'[!=&|]', Operator),318 (r'[()]', Punctuation),319 (r'[0-9]+', Number.Integer),320 (r"'(''|[^'])*'", String.Single),321 (r'"(""|[^"])*"', String.Double),322 (r'\S+', Text),323 ],324 # Help text is indented, multi-line and ends when a lower indentation325 # level is detected.326 'help': [327 # Skip blank lines after help token, if any328 (r'\s*\n', Text),329 # Determine the first help line's indentation level heuristically(!).330 # Attention: this is not perfect, but works for 99% of "normal"331 # indentation schemes up to a max. indentation level of 7.332 call_indent(7),333 call_indent(6),334 call_indent(5),335 call_indent(4),336 call_indent(3),337 call_indent(2),338 call_indent(1),339 default('#pop'), # for incomplete help sections without text340 ],341 # Handle text for indentation levels 7 to 1342 'indent7': do_indent(7),343 'indent6': do_indent(6),344 'indent5': do_indent(5),345 'indent4': do_indent(4),346 'indent3': do_indent(3),347 'indent2': do_indent(2),348 'indent1': do_indent(1),349 }350 351 352class Cfengine3Lexer(RegexLexer):353 """354 Lexer for CFEngine3 policy files.355 """356 357 name = 'CFEngine3'358 url = 'http://cfengine.org'359 aliases = ['cfengine3', 'cf3']360 filenames = ['*.cf']361 mimetypes = []362 version_added = '1.5'363 364 tokens = {365 'root': [366 (r'#.*?\n', Comment),367 (r'(body)(\s+)(\S+)(\s+)(control)',368 bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword)),369 (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)(\()',370 bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function, Punctuation),371 'arglist'),372 (r'(body|bundle)(\s+)(\S+)(\s+)(\w+)',373 bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Function)),374 (r'(")([^"]+)(")(\s+)(string|slist|int|real)(\s*)(=>)(\s*)',375 bygroups(Punctuation, Name.Variable, Punctuation,376 Whitespace, Keyword.Type, Whitespace, Operator, Whitespace)),377 (r'(\S+)(\s*)(=>)(\s*)',378 bygroups(Keyword.Reserved, Whitespace, Operator, Text)),379 (r'"', String, 'string'),380 (r'(\w+)(\()', bygroups(Name.Function, Punctuation)),381 (r'([\w.!&|()]+)(::)', bygroups(Name.Class, Punctuation)),382 (r'(\w+)(:)', bygroups(Keyword.Declaration, Punctuation)),383 (r'@[{(][^)}]+[})]', Name.Variable),384 (r'[(){},;]', Punctuation),385 (r'=>', Operator),386 (r'->', Operator),387 (r'\d+\.\d+', Number.Float),388 (r'\d+', Number.Integer),389 (r'\w+', Name.Function),390 (r'\s+', Whitespace),391 ],392 'string': [393 (r'\$[{(]', String.Interpol, 'interpol'),394 (r'\\.', String.Escape),395 (r'"', String, '#pop'),396 (r'\n', String),397 (r'.', String),398 ],399 'interpol': [400 (r'\$[{(]', String.Interpol, '#push'),401 (r'[})]', String.Interpol, '#pop'),402 (r'[^${()}]+', String.Interpol),403 ],404 'arglist': [405 (r'\)', Punctuation, '#pop'),406 (r',', Punctuation),407 (r'\w+', Name.Variable),408 (r'\s+', Whitespace),409 ],410 }411 412 413class ApacheConfLexer(RegexLexer):414 """415 Lexer for configuration files following the Apache config file416 format.417 """418 419 name = 'ApacheConf'420 aliases = ['apacheconf', 'aconf', 'apache']421 filenames = ['.htaccess', 'apache.conf', 'apache2.conf']422 mimetypes = ['text/x-apacheconf']423 url = 'https://httpd.apache.org/docs/current/configuring.html'424 version_added = '0.6'425 flags = re.MULTILINE | re.IGNORECASE426 427 tokens = {428 'root': [429 (r'\s+', Whitespace),430 (r'#(.*\\\n)+.*$|(#.*?)$', Comment),431 (r'(<[^\s>/][^\s>]*)(?:(\s+)(.*))?(>)',432 bygroups(Name.Tag, Whitespace, String, Name.Tag)),433 (r'(</[^\s>]+)(>)',434 bygroups(Name.Tag, Name.Tag)),435 (r'[a-z]\w*', Name.Builtin, 'value'),436 (r'\.+', Text),437 ],438 'value': [439 (r'\\\n', Text),440 (r'\n+', Whitespace, '#pop'),441 (r'\\', Text),442 (r'[^\S\n]+', Whitespace),443 (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number),444 (r'\d+', Number),445 (r'/([*a-z0-9][*\w./-]+)', String.Other),446 (r'(on|off|none|any|all|double|email|dns|min|minimal|'447 r'os|productonly|full|emerg|alert|crit|error|warn|'448 r'notice|info|debug|registry|script|inetd|standalone|'449 r'user|group)\b', Keyword),450 (r'"([^"\\]*(?:\\(.|\n)[^"\\]*)*)"', String.Double),451 (r'[^\s"\\]+', Text)452 ],453 }454 455 456class SquidConfLexer(RegexLexer):457 """458 Lexer for squid configuration files.459 """460 461 name = 'SquidConf'462 url = 'http://www.squid-cache.org/'463 aliases = ['squidconf', 'squid.conf', 'squid']464 filenames = ['squid.conf']465 mimetypes = ['text/x-squidconf']466 version_added = '0.9'467 flags = re.IGNORECASE468 469 keywords = (470 "access_log", "acl", "always_direct", "announce_host",471 "announce_period", "announce_port", "announce_to", "anonymize_headers",472 "append_domain", "as_whois_server", "auth_param_basic",473 "authenticate_children", "authenticate_program", "authenticate_ttl",474 "broken_posts", "buffered_logs", "cache_access_log", "cache_announce",475 "cache_dir", "cache_dns_program", "cache_effective_group",476 "cache_effective_user", "cache_host", "cache_host_acl",477 "cache_host_domain", "cache_log", "cache_mem", "cache_mem_high",478 "cache_mem_low", "cache_mgr", "cachemgr_passwd", "cache_peer",479 "cache_peer_access", "cache_replacement_policy", "cache_stoplist",480 "cache_stoplist_pattern", "cache_store_log", "cache_swap",481 "cache_swap_high", "cache_swap_log", "cache_swap_low", "client_db",482 "client_lifetime", "client_netmask", "connect_timeout", "coredump_dir",483 "dead_peer_timeout", "debug_options", "delay_access", "delay_class",484 "delay_initial_bucket_level", "delay_parameters", "delay_pools",485 "deny_info", "dns_children", "dns_defnames", "dns_nameservers",486 "dns_testnames", "emulate_httpd_log", "err_html_text",487 "fake_user_agent", "firewall_ip", "forwarded_for", "forward_snmpd_port",488 "fqdncache_size", "ftpget_options", "ftpget_program", "ftp_list_width",489 "ftp_passive", "ftp_user", "half_closed_clients", "header_access",490 "header_replace", "hierarchy_stoplist", "high_response_time_warning",491 "high_page_fault_warning", "hosts_file", "htcp_port", "http_access",492 "http_anonymizer", "httpd_accel", "httpd_accel_host",493 "httpd_accel_port", "httpd_accel_uses_host_header",494 "httpd_accel_with_proxy", "http_port", "http_reply_access",495 "icp_access", "icp_hit_stale", "icp_port", "icp_query_timeout",496 "ident_lookup", "ident_lookup_access", "ident_timeout",497 "incoming_http_average", "incoming_icp_average", "inside_firewall",498 "ipcache_high", "ipcache_low", "ipcache_size", "local_domain",499 "local_ip", "logfile_rotate", "log_fqdn", "log_icp_queries",500 "log_mime_hdrs", "maximum_object_size", "maximum_single_addr_tries",501 "mcast_groups", "mcast_icp_query_timeout", "mcast_miss_addr",502 "mcast_miss_encode_key", "mcast_miss_port", "memory_pools",503 "memory_pools_limit", "memory_replacement_policy", "mime_table",504 "min_http_poll_cnt", "min_icp_poll_cnt", "minimum_direct_hops",505 "minimum_object_size", "minimum_retry_timeout", "miss_access",506 "negative_dns_ttl", "negative_ttl", "neighbor_timeout",507 "neighbor_type_domain", "netdb_high", "netdb_low", "netdb_ping_period",508 "netdb_ping_rate", "never_direct", "no_cache", "passthrough_proxy",509 "pconn_timeout", "pid_filename", "pinger_program", "positive_dns_ttl",510 "prefer_direct", "proxy_auth", "proxy_auth_realm", "query_icmp",511 "quick_abort", "quick_abort_max", "quick_abort_min",512 "quick_abort_pct", "range_offset_limit", "read_timeout",513 "redirect_children", "redirect_program",514 "redirect_rewrites_host_header", "reference_age",515 "refresh_pattern", "reload_into_ims", "request_body_max_size",516 "request_size", "request_timeout", "shutdown_lifetime",517 "single_parent_bypass", "siteselect_timeout", "snmp_access",518 "snmp_incoming_address", "snmp_port", "source_ping", "ssl_proxy",519 "store_avg_object_size", "store_objects_per_bucket",520 "strip_query_terms", "swap_level1_dirs", "swap_level2_dirs",521 "tcp_incoming_address", "tcp_outgoing_address", "tcp_recv_bufsize",522 "test_reachability", "udp_hit_obj", "udp_hit_obj_size",523 "udp_incoming_address", "udp_outgoing_address", "unique_hostname",524 "unlinkd_program", "uri_whitespace", "useragent_log",525 "visible_hostname", "wais_relay", "wais_relay_host", "wais_relay_port",526 )527 528 opts = (529 "proxy-only", "weight", "ttl", "no-query", "default", "round-robin",530 "multicast-responder", "on", "off", "all", "deny", "allow", "via",531 "parent", "no-digest", "heap", "lru", "realm", "children", "q1", "q2",532 "credentialsttl", "none", "disable", "offline_toggle", "diskd",533 )534 535 actions = (536 "shutdown", "info", "parameter", "server_list", "client_list",537 r'squid.conf',538 )539 540 actions_stats = (541 "objects", "vm_objects", "utilization", "ipcache", "fqdncache", "dns",542 "redirector", "io", "reply_headers", "filedescriptors", "netdb",543 )544 545 actions_log = ("status", "enable", "disable", "clear")546 547 acls = (548 "url_regex", "urlpath_regex", "referer_regex", "port", "proto",549 "req_mime_type", "rep_mime_type", "method", "browser", "user", "src",550 "dst", "time", "dstdomain", "ident", "snmp_community",551 )552 553 ipv4_group = r'(\d+|0x[0-9a-f]+)'554 ipv4 = rf'({ipv4_group}(\.{ipv4_group}){{3}})'555 ipv6_group = r'([0-9a-f]{0,4})'556 ipv6 = rf'({ipv6_group}(:{ipv6_group}){{1,7}})'557 bare_ip = rf'({ipv4}|{ipv6})'558 # XXX: /integer is a subnet mark, but what is /IP ?559 # There is no test where it is used.560 ip = rf'{bare_ip}(/({bare_ip}|\d+))?'561 562 tokens = {563 'root': [564 (r'\s+', Whitespace),565 (r'#', Comment, 'comment'),566 (words(keywords, prefix=r'\b', suffix=r'\b'), Keyword),567 (words(opts, prefix=r'\b', suffix=r'\b'), Name.Constant),568 # Actions569 (words(actions, prefix=r'\b', suffix=r'\b'), String),570 (words(actions_stats, prefix=r'stats/', suffix=r'\b'), String),571 (words(actions_log, prefix=r'log/', suffix=r'='), String),572 (words(acls, prefix=r'\b', suffix=r'\b'), Keyword),573 (ip, Number.Float),574 (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number),575 (r'\S+', Text),576 ],577 'comment': [578 (r'\s*TAG:.*', String.Escape, '#pop'),579 (r'.+', Comment, '#pop'),580 default('#pop'),581 ],582 }583 584 585class NginxConfLexer(RegexLexer):586 """587 Lexer for Nginx configuration files.588 """589 name = 'Nginx configuration file'590 url = 'http://nginx.net/'591 aliases = ['nginx']592 filenames = ['nginx.conf']593 mimetypes = ['text/x-nginx-conf']594 version_added = '0.11'595 596 tokens = {597 'root': [598 (r'(include)(\s+)([^\s;]+)', bygroups(Keyword, Whitespace, Name)),599 (r'[^\s;#]+', Keyword, 'stmt'),600 include('base'),601 ],602 'block': [603 (r'\}', Punctuation, '#pop:2'),604 (r'[^\s;#]+', Keyword.Namespace, 'stmt'),605 include('base'),606 ],607 'stmt': [608 (r'\{', Punctuation, 'block'),609 (r';', Punctuation, '#pop'),610 include('base'),611 ],612 'base': [613 (r'#.*\n', Comment.Single),614 (r'on|off', Name.Constant),615 (r'\$[^\s;#()]+', Name.Variable),616 (r'([a-z0-9.-]+)(:)([0-9]+)',617 bygroups(Name, Punctuation, Number.Integer)),618 (r'[a-z-]+/[a-z-+]+', String), # mimetype619 # (r'[a-zA-Z._-]+', Keyword),620 (r'[0-9]+[km]?\b', Number.Integer),621 (r'(~)(\s*)([^\s{]+)', bygroups(Punctuation, Whitespace, String.Regex)),622 (r'[:=~]', Punctuation),623 (r'[^\s;#{}$]+', String), # catch all624 (r'/[^\s;#]*', Name), # pathname625 (r'\s+', Whitespace),626 (r'[$;]', Text), # leftover characters627 ],628 }629 630 631class LighttpdConfLexer(RegexLexer):632 """633 Lexer for Lighttpd configuration files.634 """635 name = 'Lighttpd configuration file'636 url = 'http://lighttpd.net/'637 aliases = ['lighttpd', 'lighty']638 filenames = ['lighttpd.conf']639 mimetypes = ['text/x-lighttpd-conf']640 version_added = '0.11'641 642 tokens = {643 'root': [644 (r'#.*\n', Comment.Single),645 (r'/\S*', Name), # pathname646 (r'[a-zA-Z._-]+', Keyword),647 (r'\d+\.\d+\.\d+\.\d+(?:/\d+)?', Number),648 (r'[0-9]+', Number),649 (r'=>|=~|\+=|==|=|\+', Operator),650 (r'\$[A-Z]+', Name.Builtin),651 (r'[(){}\[\],]', Punctuation),652 (r'"([^"\\]*(?:\\.[^"\\]*)*)"', String.Double),653 (r'\s+', Whitespace),654 ],655 656 }657 658 659class DockerLexer(RegexLexer):660 """661 Lexer for Docker configuration files.662 """663 name = 'Docker'664 url = 'http://docker.io'665 aliases = ['docker', 'dockerfile']666 filenames = ['Dockerfile', '*.docker']667 mimetypes = ['text/x-dockerfile-config']668 version_added = '2.0'669 670 _keywords = (r'(?:MAINTAINER|EXPOSE|WORKDIR|USER|STOPSIGNAL)')671 _bash_keywords = (r'(?:RUN|CMD|ENTRYPOINT|ENV|ARG|LABEL|ADD|COPY)')672 _lb = r'(?:\s*\\?\s*)' # dockerfile line break regex673 flags = re.IGNORECASE | re.MULTILINE674 675 tokens = {676 'root': [677 (r'#.*', Comment),678 (r'(FROM)([ \t]*)(\S*)([ \t]*)(?:(AS)([ \t]*)(\S*))?',679 bygroups(Keyword, Whitespace, String, Whitespace, Keyword, Whitespace, String)),680 (rf'(ONBUILD)(\s+)({_lb})', bygroups(Keyword, Whitespace, using(BashLexer))),681 (rf'(HEALTHCHECK)(\s+)(({_lb}--\w+=\w+{_lb})*)',682 bygroups(Keyword, Whitespace, using(BashLexer))),683 (rf'(VOLUME|ENTRYPOINT|CMD|SHELL)(\s+)({_lb})(\[.*?\])',684 bygroups(Keyword, Whitespace, using(BashLexer), using(JsonLexer))),685 (rf'(LABEL|ENV|ARG)(\s+)(({_lb}\w+=\w+{_lb})*)',686 bygroups(Keyword, Whitespace, using(BashLexer))),687 (rf'({_keywords}|VOLUME)\b(\s+)(.*)', bygroups(Keyword, Whitespace, String)),688 (rf'({_bash_keywords})(\s+)', bygroups(Keyword, Whitespace)),689 (r'(.*\\\n)*.+', using(BashLexer)),690 ]691 }692 693 694class TerraformLexer(ExtendedRegexLexer):695 """696 Lexer for terraformi ``.tf`` files.697 """698 699 name = 'Terraform'700 url = 'https://www.terraform.io/'701 aliases = ['terraform', 'tf', 'hcl']702 filenames = ['*.tf', '*.hcl']703 mimetypes = ['application/x-tf', 'application/x-terraform']704 version_added = '2.1'705 706 classes = ('backend', 'data', 'module', 'output', 'provider',707 'provisioner', 'resource', 'variable')708 classes_re = "({})".format(('|').join(classes))709 710 types = ('string', 'number', 'bool', 'list', 'tuple', 'map', 'set', 'object', 'null')711 712 numeric_functions = ('abs', 'ceil', 'floor', 'log', 'max',713 'mix', 'parseint', 'pow', 'signum')714 715 string_functions = ('chomp', 'format', 'formatlist', 'indent',716 'join', 'lower', 'regex', 'regexall', 'replace',717 'split', 'strrev', 'substr', 'title', 'trim',718 'trimprefix', 'trimsuffix', 'trimspace', 'upper'719 )720 721 collection_functions = ('alltrue', 'anytrue', 'chunklist', 'coalesce',722 'coalescelist', 'compact', 'concat', 'contains',723 'distinct', 'element', 'flatten', 'index', 'keys',724 'length', 'list', 'lookup', 'map', 'matchkeys',725 'merge', 'range', 'reverse', 'setintersection',726 'setproduct', 'setsubtract', 'setunion', 'slice',727 'sort', 'sum', 'transpose', 'values', 'zipmap'728 )729 730 encoding_functions = ('base64decode', 'base64encode', 'base64gzip',731 'csvdecode', 'jsondecode', 'jsonencode', 'textdecodebase64',732 'textencodebase64', 'urlencode', 'yamldecode', 'yamlencode')733 734 filesystem_functions = ('abspath', 'dirname', 'pathexpand', 'basename',735 'file', 'fileexists', 'fileset', 'filebase64', 'templatefile')736 737 date_time_functions = ('formatdate', 'timeadd', 'timestamp')738 739 hash_crypto_functions = ('base64sha256', 'base64sha512', 'bcrypt', 'filebase64sha256',740 'filebase64sha512', 'filemd5', 'filesha1', 'filesha256', 'filesha512',741 'md5', 'rsadecrypt', 'sha1', 'sha256', 'sha512', 'uuid', 'uuidv5')742 743 ip_network_functions = ('cidrhost', 'cidrnetmask', 'cidrsubnet', 'cidrsubnets')744 745 type_conversion_functions = ('can', 'defaults', 'tobool', 'tolist', 'tomap',746 'tonumber', 'toset', 'tostring', 'try')747 748 builtins = numeric_functions + string_functions + collection_functions + encoding_functions +\749 filesystem_functions + date_time_functions + hash_crypto_functions + ip_network_functions +\750 type_conversion_functions751 builtins_re = "({})".format(('|').join(builtins))752 753 def heredoc_callback(self, match, ctx):754 # Parse a terraform heredoc755 # match: 1 = <<[-]?, 2 = name 3 = rest of line756 757 start = match.start(1)758 yield start, Operator, match.group(1) # <<[-]?759 yield match.start(2), String.Delimiter, match.group(2) # heredoc name760 761 ctx.pos = match.start(3)762 ctx.end = match.end(3)763 yield ctx.pos, String.Heredoc, match.group(3)764 ctx.pos = match.end()765 766 hdname = match.group(2)767 tolerant = True # leading whitespace is always accepted768 769 lines = []770 771 for match in line_re.finditer(ctx.text, ctx.pos):772 if tolerant:773 check = match.group().strip()774 else:775 check = match.group().rstrip()776 if check == hdname:777 for amatch in lines:778 yield amatch.start(), String.Heredoc, amatch.group()779 yield match.start(), String.Delimiter, match.group()780 ctx.pos = match.end()781 break782 else:783 lines.append(match)784 else:785 # end of heredoc not found -- error!786 for amatch in lines:787 yield amatch.start(), Error, amatch.group()788 ctx.end = len(ctx.text)789 790 tokens = {791 'root': [792 include('basic'),793 include('whitespace'),794 795 # Strings796 (r'(".*")', bygroups(String.Double)),797 798 # Constants799 (words(('true', 'false'), prefix=r'\b', suffix=r'\b'), Name.Constant),800 801 # Types802 (words(types, prefix=r'\b', suffix=r'\b'), Keyword.Type),803 804 include('identifier'),805 include('punctuation'),806 (r'[0-9]+', Number),807 ],808 'basic': [809 (r'\s*/\*', Comment.Multiline, 'comment'),810 (r'\s*(#|//).*\n', Comment.Single),811 include('whitespace'),812 813 # e.g. terraform {814 # e.g. egress {815 (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=?)(\s*)(\{)',816 bygroups(Whitespace, Name.Builtin, Whitespace, Operator, Whitespace, Punctuation)),817 818 # Assignment with attributes, e.g. something = ...819 (r'(\s*)([0-9a-zA-Z-_]+)(\s*)(=)(\s*)',820 bygroups(Whitespace, Name.Attribute, Whitespace, Operator, Whitespace)),821 822 # Assignment with environment variables and similar, e.g. "something" = ...823 # or key value assignment, e.g. "SlotName" : ...824 (r'(\s*)("\S+")(\s*)([=:])(\s*)',825 bygroups(Whitespace, Literal.String.Double, Whitespace, Operator, Whitespace)),826 827 # Functions, e.g. jsonencode(element("value"))828 (builtins_re + r'(\()', bygroups(Name.Function, Punctuation)),829 830 # List of attributes, e.g. ignore_changes = [last_modified, filename]831 (r'(\[)([a-z_,\s]+)(\])', bygroups(Punctuation, Name.Builtin, Punctuation)),832 833 # e.g. resource "aws_security_group" "allow_tls" {834 # e.g. backend "consul" {835 (classes_re + r'(\s+)("[0-9a-zA-Z-_]+")?(\s*)("[0-9a-zA-Z-_]+")(\s+)(\{)',836 bygroups(Keyword.Reserved, Whitespace, Name.Class, Whitespace, Name.Variable, Whitespace, Punctuation)),837 838 # here-doc style delimited strings839 (r'(<<-?)\s*([a-zA-Z_]\w*)(.*?\n)', heredoc_callback),840 ],841 'identifier': [842 (r'\b(var\.[0-9a-zA-Z-_\.\[\]]+)\b', bygroups(Name.Variable)),843 (r'\b([0-9a-zA-Z-_\[\]]+\.[0-9a-zA-Z-_\.\[\]]+)\b',844 bygroups(Name.Variable)),845 ],846 'punctuation': [847 (r'[\[\]()\{\},.?:!=]', Punctuation),848 ],849 'comment': [850 (r'[^*/]', Comment.Multiline),851 (r'/\*', Comment.Multiline, '#push'),852 (r'\*/', Comment.Multiline, '#pop'),853 (r'[*/]', Comment.Multiline)854 ],855 'whitespace': [856 (r'\n', Whitespace),857 (r'\s+', Whitespace),858 (r'(\\)(\n)', bygroups(Text, Whitespace)),859 ],860 }861 862 863class TermcapLexer(RegexLexer):864 """865 Lexer for termcap database source.866 867 This is very simple and minimal.868 """869 name = 'Termcap'870 aliases = ['termcap']871 filenames = ['termcap', 'termcap.src']872 mimetypes = []873 url = 'https://en.wikipedia.org/wiki/Termcap'874 version_added = '2.1'875 876 # NOTE:877 # * multiline with trailing backslash878 # * separator is ':'879 # * to embed colon as data, we must use \072880 # * space after separator is not allowed (mayve)881 tokens = {882 'root': [883 (r'^#.*', Comment),884 (r'^[^\s#:|]+', Name.Tag, 'names'),885 (r'\s+', Whitespace),886 ],887 'names': [888 (r'\n', Whitespace, '#pop'),889 (r':', Punctuation, 'defs'),890 (r'\|', Punctuation),891 (r'[^:|]+', Name.Attribute),892 ],893 'defs': [894 (r'(\\)(\n[ \t]*)', bygroups(Text, Whitespace)),895 (r'\n[ \t]*', Whitespace, '#pop:2'),896 (r'(#)([0-9]+)', bygroups(Operator, Number)),897 (r'=', Operator, 'data'),898 (r':', Punctuation),899 (r'[^\s:=#]+', Name.Class),900 ],901 'data': [902 (r'\\072', Literal),903 (r':', Punctuation, '#pop'),904 (r'[^:\\]+', Literal), # for performance905 (r'.', Literal),906 ],907 }908 909 910class TerminfoLexer(RegexLexer):911 """912 Lexer for terminfo database source.913 914 This is very simple and minimal.915 """916 name = 'Terminfo'917 aliases = ['terminfo']918 filenames = ['terminfo', 'terminfo.src']919 mimetypes = []920 url = 'https://en.wikipedia.org/wiki/Terminfo'921 version_added = '2.1'922 923 # NOTE:924 # * multiline with leading whitespace925 # * separator is ','926 # * to embed comma as data, we can use \,927 # * space after separator is allowed928 tokens = {929 'root': [930 (r'^#.*$', Comment),931 (r'^[^\s#,|]+', Name.Tag, 'names'),932 (r'\s+', Whitespace),933 ],934 'names': [935 (r'\n', Whitespace, '#pop'),936 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), 'defs'),937 (r'\|', Punctuation),938 (r'[^,|]+', Name.Attribute),939 ],940 'defs': [941 (r'\n[ \t]+', Whitespace),942 (r'\n', Whitespace, '#pop:2'),943 (r'(#)([0-9]+)', bygroups(Operator, Number)),944 (r'=', Operator, 'data'),945 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace)),946 (r'[^\s,=#]+', Name.Class),947 ],948 'data': [949 (r'\\[,\\]', Literal),950 (r'(,)([ \t]*)', bygroups(Punctuation, Whitespace), '#pop'),951 (r'[^\\,]+', Literal), # for performance952 (r'.', Literal),953 ],954 }955 956 957class PkgConfigLexer(RegexLexer):958 """959 Lexer for pkg-config960 (see also `manual page <http://linux.die.net/man/1/pkg-config>`_).961 """962 963 name = 'PkgConfig'964 url = 'http://www.freedesktop.org/wiki/Software/pkg-config/'965 aliases = ['pkgconfig']966 filenames = ['*.pc']967 mimetypes = []968 version_added = '2.1'969 970 tokens = {971 'root': [972 (r'#.*$', Comment.Single),973 974 # variable definitions975 (r'^(\w+)(=)', bygroups(Name.Attribute, Operator)),976 977 # keyword lines978 (r'^([\w.]+)(:)',979 bygroups(Name.Tag, Punctuation), 'spvalue'),980 981 # variable references982 include('interp'),983 984 # fallback985 (r'\s+', Whitespace),986 (r'[^${}#=:\n.]+', Text),987 (r'.', Text),988 ],989 'interp': [990 # you can escape literal "$" as "$$"991 (r'\$\$', Text),992 993 # variable references994 (r'\$\{', String.Interpol, 'curly'),995 ],996 'curly': [997 (r'\}', String.Interpol, '#pop'),998 (r'\w+', Name.Attribute),999 ],1000 'spvalue': [1001 include('interp'),1002 1003 (r'#.*$', Comment.Single, '#pop'),1004 (r'\n', Whitespace, '#pop'),1005 1006 # fallback1007 (r'\s+', Whitespace),1008 (r'[^${}#\n\s]+', Text),1009 (r'.', Text),1010 ],1011 }1012 1013 1014class PacmanConfLexer(RegexLexer):1015 """1016 Lexer for pacman.conf.1017 1018 Actually, IniLexer works almost fine for this format,1019 but it yield error token. It is because pacman.conf has1020 a form without assignment like:1021 1022 UseSyslog1023 Color1024 TotalDownload1025 CheckSpace1026 VerbosePkgLists1027 1028 These are flags to switch on.1029 """1030 1031 name = 'PacmanConf'1032 url = 'https://www.archlinux.org/pacman/pacman.conf.5.html'1033 aliases = ['pacmanconf']1034 filenames = ['pacman.conf']1035 mimetypes = []1036 version_added = '2.1'1037 1038 tokens = {1039 'root': [1040 # comment1041 (r'#.*$', Comment.Single),1042 1043 # section header1044 (r'^(\s*)(\[.*?\])(\s*)$', bygroups(Whitespace, Keyword, Whitespace)),1045 1046 # variable definitions1047 # (Leading space is allowed...)1048 (r'(\w+)(\s*)(=)',1049 bygroups(Name.Attribute, Whitespace, Operator)),1050 1051 # flags to on1052 (r'^(\s*)(\w+)(\s*)$',1053 bygroups(Whitespace, Name.Attribute, Whitespace)),1054 1055 # built-in special values1056 (words((1057 '$repo', # repository1058 '$arch', # architecture1059 '%o', # outfile1060 '%u', # url1061 ), suffix=r'\b'),1062 Name.Variable),1063 1064 # fallback1065 (r'\s+', Whitespace),1066 (r'.', Text),1067 ],1068 }1069 1070 1071class AugeasLexer(RegexLexer):1072 """1073 Lexer for Augeas.1074 """1075 name = 'Augeas'1076 url = 'http://augeas.net'1077 aliases = ['augeas']1078 filenames = ['*.aug']1079 version_added = '2.4'1080 1081 tokens = {1082 'root': [1083 (r'(module)(\s*)([^\s=]+)', bygroups(Keyword.Namespace, Whitespace, Name.Namespace)),1084 (r'(let)(\s*)([^\s=]+)', bygroups(Keyword.Declaration, Whitespace, Name.Variable)),1085 (r'(del|store|value|counter|seq|key|label|autoload|incl|excl|transform|test|get|put)(\s+)', bygroups(Name.Builtin, Whitespace)),1086 (r'(\()([^:]+)(\:)(unit|string|regexp|lens|tree|filter)(\))', bygroups(Punctuation, Name.Variable, Punctuation, Keyword.Type, Punctuation)),1087 (r'\(\*', Comment.Multiline, 'comment'),1088 (r'[*+\-.;=?|]', Operator),1089 (r'[()\[\]{}]', Operator),1090 (r'"', String.Double, 'string'),1091 (r'\/', String.Regex, 'regex'),1092 (r'([A-Z]\w*)(\.)(\w+)', bygroups(Name.Namespace, Punctuation, Name.Variable)),1093 (r'.', Name.Variable),1094 (r'\s+', Whitespace),1095 ],1096 'string': [1097 (r'\\.', String.Escape),1098 (r'[^"]', String.Double),1099 (r'"', String.Double, '#pop'),1100 ],1101 'regex': [1102 (r'\\.', String.Escape),1103 (r'[^/]', String.Regex),1104 (r'\/', String.Regex, '#pop'),1105 ],1106 'comment': [1107 (r'[^*)]', Comment.Multiline),1108 (r'\(\*', Comment.Multiline, '#push'),1109 (r'\*\)', Comment.Multiline, '#pop'),1110 (r'[)*]', Comment.Multiline)1111 ],1112 }1113 1114 1115class TOMLLexer(RegexLexer):1116 """1117 Lexer for TOML, a simple language for config files.1118 """1119 1120 name = 'TOML'1121 aliases = ['toml']1122 filenames = ['*.toml', 'Pipfile', 'poetry.lock']1123 mimetypes = ['application/toml']1124 url = 'https://toml.io'1125 version_added = '2.4'1126 1127 # Based on the TOML spec: https://toml.io/en/v1.0.01128 1129 # The following is adapted from CPython's tomllib:1130 _time = r"\d\d:\d\d(:\d\d(\.\d+)?)?"1131 _datetime = rf"""(?x)1132 \d\d\d\d-\d\d-\d\d # date, e.g., 1988-10-271133 (1134 [Tt ] {_time} # optional time1135 (1136 [Zz]|[+-]\d\d:\d\d # optional time offset1137 )?1138 )?1139 """1140 1141 tokens = {1142 'root': [1143 # Note that we make an effort in order to distinguish1144 # moments at which we're parsing a key and moments at1145 # which we're parsing a value. In the TOML code1146 #1147 # 1234 = 12341148 #1149 # the first "1234" should be Name, the second Integer.1150 1151 # Whitespace1152 (r'\s+', Whitespace),1153 1154 # Comment1155 (r'#.*', Comment.Single),1156 1157 # Assignment keys1158 include('key'),1159 1160 # After "=", find a value1161 (r'(=)(\s*)', bygroups(Operator, Whitespace), 'value'),1162 1163 # Table header1164 (r'\[\[?', Keyword, 'table-key'),1165 ],1166 'key': [1167 # Start of bare key (only ASCII is allowed here).1168 (r'[A-Za-z0-9_-]+', Name),1169 # Quoted key1170 (r'"', String.Double, 'basic-string'),1171 (r"'", String.Single, 'literal-string'),1172 # Dots act as separators in keys1173 (r'\.', Punctuation),1174 ],1175 'table-key': [1176 # This is like 'key', but highlights the name components1177 # and separating dots as Keyword because it looks better1178 # when the whole table header is Keyword. We do highlight1179 # strings as strings though.1180 # Start of bare key (only ASCII is allowed here).1181 (r'[A-Za-z0-9_-]+', Keyword),1182 (r'"', String.Double, 'basic-string'),1183 (r"'", String.Single, 'literal-string'),1184 (r'\.', Keyword),1185 (r'\]\]?', Keyword, '#pop'),1186 1187 # Inline whitespace allowed1188 (r'[ \t]+', Whitespace),1189 ],1190 'value': [1191 # Datetime, baretime1192 (_datetime, Literal.Date, '#pop'),1193 (_time, Literal.Date, '#pop'),1194 1195 # Recognize as float if there is a fractional part1196 # and/or an exponent.1197 (r'[+-]?\d[0-9_]*[eE][+-]?\d[0-9_]*', Number.Float, '#pop'),1198 (r'[+-]?\d[0-9_]*\.\d[0-9_]*([eE][+-]?\d[0-9_]*)?',1199 Number.Float, '#pop'),1200 