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