# Compiled Texinfo document
# Reference: https://www2.pd.infn.it/TeX/doc/html/texinfo/info_2.html#SEC13
name: "Info"
scopeName: "text.info"
fileTypes: ["info"]
firstLineMatch: """(?x)
	^(?:INFO-DIR-SECTION|START-INFO-DIR-ENTRY)(?:$|\\s) |
	^(?-x:This is .*?\\.info, produced by (?:makeinfo|texi2))
"""
patterns: [include: "#main"]

repository:
	main:
		patterns: [
			{include: "#node"}
			{include: "#dir"}
			{include: "#nodeInnards"}
		]


	# Multiple consecutive asterisks
	border:
		name:  "markup.heading.info"
		match: "^((?:\\*|=|-){2,})[ \\t]*$"
		captures:
			1: name: "punctuation.definition.heading.info"


	# Directives generated by `@dircategory` and `@direntry`
	dir:
		patterns: [{
			name:  "meta.dir-section.info"
			begin: "^INFO-DIR-SECTION(?=\\s|$)"
			end:   "[^\\s\\x1F][^\\x1F]*"
			beginCaptures: 0: name: "keyword.operator.dir-section.info"
			endCaptures:   0: name: "entity.name.dir-section.info"
		},{
			name:  "meta.dir-entry.info"
			begin: "^START-INFO-DIR-ENTRY(?=\\s|$)"
			end:   "^END-INFO-DIR-ENTRY(?=\\s|$)"
			beginCaptures: 0: name: "keyword.operator.dir-entry.begin.info"
			endCaptures:   0: name: "keyword.operator.dir-entry.end.info"
			patterns: [include: "#menuItem"]
		}]


	# A “problematic” character surrounded by ASCII DEL characters (U+007F)
	escape:
		name:  "string.quoted.other.info"
		begin: "\\x7F(?=.*?\\x7F)"
		end:   "\\x7F"
		beginCaptures: 0: name: "punctuation.definition.escape.c0.ctrl-char.delete.begin.info"
		endCaptures:   0: name: "punctuation.definition.escape.c0.ctrl-char.delete.end.info"


	# An image with HTML-ish attributes
	image:
		name:  "meta.image.info"
		begin: "(\\x00)(\\x08)(\\[)(image)"
		end:   "(\\x00)(\\x08)(\\])"
		beginCaptures:
			1: name: "punctuation.c0.ctrl-char.null-byte.info"
			2: name: "punctuation.c0.ctrl-char.backspace.info"
			3: name: "punctuation.definition.image.begin.info"
			4: name: "storage.type.image.info"
		endCaptures:
			1: name: "punctuation.c0.ctrl-char.null-byte.info"
			2: name: "punctuation.c0.ctrl-char.backspace.info"
			3: name: "punctuation.definition.image.end.info"
		patterns: [{
			# Attribute: name="value"
			name:  "meta.$1-attribute.info"
			begin: "([a-z]+)(=)"
			end:   '((?!")[^\\s\\x00]+)|(?<=")|(?=\\x00\\x08\\])'
			beginCaptures:
				1: name: "entity.other.attribute-name.info"
				2: name: "punctuation.separator.key-value.info"
			endCaptures:
				1: name: "string.unquoted.info"
			patterns: [{
				# "Double-quoted" string
				name:  "string.quoted.double.info"
				begin: '"'
				end:   '"'
				beginCaptures: 0: name: "punctuation.definition.string.begin.info"
				endCaptures:   0: name: "punctuation.definition.string.end.info"
				patterns: [{
					# Escaped character (doesn't work in unquoted values)
					name: "constant.character.escape.backslash.info"
					match: "(\\\\)."
					captures:
						1: name: "punctuation.definition.escape.info"
				}]
			}]
		},{
			# Mark malformed attributes as errors
			name: "invalid.illegal.bad-attribute.info"
			match: "[^\\s\\x00]+"
		}]


	# Output of `@printindex` command
	index:
		name:  "meta.index.info"
		match: "(\\x00)(\\x08)(\\[)(index)(\\x00)(\\x08)(\\])"
		captures:
			1: name: "punctuation.c0.ctrl-char.null-byte.info"
			2: name: "punctuation.c0.ctrl-char.backspace.info"
			3: name: "punctuation.definition.index.begin.info"
			4: name: "storage.type.index.info"
			5: name: "punctuation.c0.ctrl-char.null-byte.info"
			6: name: "punctuation.c0.ctrl-char.backspace.info"
			7: name: "punctuation.definition.index.end.info"


	# Parenthesised name of another Info manual
	manualName:
		name:  "meta.manual-name.info"
		begin: "\\G(\\()(?=.*?\\))"
		end:   "\\)"
		beginCaptures: 1: name: "punctuation.definition.reference.manual.begin.info"
		endCaptures:   0: name: "punctuation.definition.reference.manual.end.info"
		contentName:   "constant.other.reference.link.info"
		patterns: [include: "#escape"]


	# Menu section, including subentries
	menu:
		name:  "meta.menu.info"
		begin: "^(\\*)\\s+([Mm]enu)(:)(.*)"
		end:   "(?=^[^\\s*]|\\x1F|\\f)"
		beginCaptures:
			0: name: "markup.list.unnumbered.menu.info"
			1: name: "punctuation.definition.list.info"
			2: name: "keyword.operator.begin-menu.info"
			3: name: "punctuation.delimiter.separator.colon.info"
			4: name: "comment.line.ignored.info"
		patterns: [
			{include: "#menuItem"}
			{include: "#nodeInnards"}
		]


	# Individual menu entries
	menuItem:
		patterns: [{
			# * Node and topic name::
			name:  "meta.topic.single-name.info"
			begin: "^(\\*)\\s+((?![\\s*\\x1F]|:\\s)(?:(?!:\\s).)+)(::)(?=\\s|$)[ \\t]*"
			end:   "(?=^[\\*\\x1F\\f]|^[ \\t]*$|\\x1F|\\f)"
			beginCaptures:
				0: name: "markup.list.unnumbered.menu.info"
				1: name: "punctuation.definition.list.info"
				2: name: "entity.name.tag.topic.info", patterns: [include: "#escape"]
				3: name: "punctuation.delimiter.separator.colon.info"
			contentName: "string.unquoted.description.info"
		},{
			# * Topic name: (node-name).
			name:  "meta.topic.info"
			begin: "^(\\*)\\s+((?![\\s*\\x1F]|:\\s)(?:(?!:\\s).)+)(:)(?=\\s|$)[ \\t]*"
			end:   "(?=^[\\*\\x1F\\f]|^[ \\t]*$|\\x1F|\\f|^END-INFO-DIR-ENTRY(?:\\s|$))"
			beginCaptures:
				0: name: "markup.list.unnumbered.menu.info"
				1: name: "punctuation.definition.list.info"
				2: name: "entity.name.tag.topic.info", patterns: [include: "#escape"]
				3: name: "punctuation.delimiter.separator.colon.info"
			patterns: [{
				# Node name, separated from label by a single `:`
				contentName: "entity.name.node.info"
				begin: "\\G(?=[^\\s.,])"
				end:   "[.,\\t]|$"
				endCaptures:
					0: name: "punctuation.terminator.topic.info"
				patterns: [
					{include: "#manualName"}
					{include: "#escape"}
				]
			},{
				# Free-form description
				name:  "string.unquoted.description.info"
				begin: "(?<=[.,\\t])[ \\t]*"
				end:   "(?=^[\\*\\x1F\\f]|^[ \\t]*$|\\x1F|\\f|^END-INFO-DIR-ENTRY(?:\\s|$))"
				patterns: [{
					begin: "\\G"
					end:   "((\\()line +\\d+(\\)))(?=[ \\t]*$)|(?=\\S|^\\s*$)"
					endCaptures:
						1: name: "constant.numeric.line-number.info"
						2: name: "punctuation.definition.line-number.begin.info"
						3: name: "punctuation.definition.line-number.end.info"
				}]
			}]
		}]


	# GNUnese for "page" or "section"
	node:
		name:  "meta.node.info"
		begin: "(\\x1F)(\\f)?"
		end:   "\\f|(?=\\x1F)"
		beginCaptures:
			1: name: "punctuation.c0.ctrl-char.unit-separator.info"
			2: name: "punctuation.whitespace.form-feed.info"
		endCaptures:
			0: name: "punctuation.whitespace.form-feed.info"
		patterns: [{
			# Header line, assumed to be first non-blank line following ^_
			begin: "(?<=\\x1F\\f|\\x1F)"
			end:   "(?:\\G|^)(?!(?i:Indirect|Tag Table|Local Variables):[ \\t]*$)([^\\x1F\\f]*?)(?=\\s*$)|(?=\\x1F|\\f)"
			endCaptures:
				0: name: "markup.heading.1.info"
				1: patterns: [include: "#pointer"]
			patterns: [include: "#tables"]
		}, include: "#nodeInnards"]


	# Stuff matched inside a `#node`
	nodeInnards:
		patterns: [
			{include: "#menu"}
			{include: "#xref"}
			{include: "#image"}
			{include: "#index"}
			{include: "#border"}
			{include: "#notes"}
		]


	# Footnotes
	notes:
		name:  "meta.footnotes.info"
		begin: "^([ \\t]+)(-+) (Footnotes) (-+)$"
		end:   "(?=\\x1F|^(?![ \\t]*$|\\1[ \\t]*\\S))"
		beginCaptures:
			1: name: "punctuation.whitespace.leading.indentation.info"
			2: name: "punctuation.definition.heading.footnotes.left.info"
			3: name: "markup.heading.3.info"
			4: name: "punctuation.definition.heading.footnotes.right.info"
		patterns: [{
			name:  "markup.list.numbered.footnote.info"
			begin: "^([ \\t]+)((\\()\\d+(\\)))[ \\t]+"
			end:   "(?=\\x1F|^(?![ \\t]*$|\\1[ \\t]*\\S))|(?=^[ \\t]+\\(\\d+\\)\\s)"
			beginCaptures:
				1: name: "punctuation.whitespace.leading.indentation.info"
				2: name: "constant.numeric.list-number.info"
				3: name: "punctuation.definition.list-number.begin.info"
				4: name: "punctuation.definition.list-number.end.info"
			contentName: "markup.quote.footnote.info"
		}]
		


	# Pointers to other Info nodes
	pointer:
		name:  "meta.pointer.info"
		begin: "(?<=^|\\G|\\s|,)(([Ff]ile|[Nn]ode|[Nn]ext|[Pp]rev|[Uu]p)(:))[ \\t]+"
		end:   " *(?:(,)|(\\t)|$)"
		beginCaptures:
			1: name: "variable.pointer.${2:/downcase}.info"
			3: name: "keyword.operator.assignment.key-value.colon.info"
		endCaptures:
			1: name: "punctuation.delimiter.comma.info"
			2: name: "punctuation.whitespace.tab.info"
		contentName: "constant.other.reference.link.info"


	# Special node types
	tables:
		patterns: [{
			# Indirect table, found in split output
			name:  "meta.indirect-table.info"
			begin: "(?i)(?:^|\\G)(Indirect)(:)[ \\t]*$"
			end:   "(?=\\f|\\x1F|^[ \\t]+\\S)"
			beginCaptures:
				1: name: "markup.heading.1.info"
				2: name: "punctuation.definition.table.colon.info"
			patterns: [{
				name:  "meta.offset.info"
				match: "^[ \\t]*([^:\\s][^:]*)(:)[ \\t]*(\\d+)[ \\t]*$"
				captures:
					1: name: "entity.name.file.filename.info"
					2: name: "keyword.operator.assignment.key-value.colon.info"
					3: name: "constant.decimal.integer.int.byte-offset.info"
			}]
		},{
			# Tag table
			name:  "meta.tag-table.info"
			begin: "(?i)(?:^|\\G)(Tag Table)(:)[ \\t]*$"
			end:   "(?=\\f|\\x1F|^[ \\t]+\\S)"
			beginCaptures:
				1: name: "markup.heading.1.info"
				2: name: "punctuation.definition.table.colon.info"
			patterns: [{
				begin: "\\G"
				end:   "^((\\()Indirect(\\)))\\s*$|(?=\\S)"
				endCaptures:
					1: name: "storage.modifier.indirect.info"
					2: name: "punctuation.definition.modifier.begin.info"
					3: name: "punctuation.definition.modifier.end.info"
			},{
				# (Node|Ref): <node-id>^?<byte-offset>
				match: "^(?:(File)(:)[ \\t]*(.*?)(,)[ \\t]+)?(Node|Ref)(:)[ \\t]*([^\\s\\x1F\\x7F][^\\x1F\\x7F]*)(\\x7F)(\\d+)[ \\t]*$"
				captures:
					1: name: "storage.type.file.info"
					2: name: "punctuation.delimiter.separator.colon.info"
					3: name: "constant.other.reference.link.info"
					4: name: "punctuation.delimiter.comma.info"
					5: name: "storage.type.tag.${1:/downcase}.info"
					6: name: "punctuation.delimiter.separator.colon.info"
					7: name: "entity.name.tag.info"
					8: name: "punctuation.separator.tag.c0.ctrl-char.delete.info"
					9: name: "constant.decimal.integer.int.byte-offset.info"
			}]
		},{
			# Local variables (Emacs)
			name:  "meta.local-variables.info"
			begin: "(?i)(?:^|\\G)(Local Variables)(:)[ \\t]*$"
			end:   "(?i)(?=\\f|\\x1F|^[ \\t]+\\S)|^(End)(:)[ \\t]*$"
			captures:
				1: name: "markup.heading.1.info"
				2: name: "punctuation.definition.table.colon.info"
			patterns: [{
				name:  "meta.local-variable.info"
				begin: "(?i)^(?!End[ \\t]*:)([-\\w]+)[ \\t]*(:)[ \\t]*"
				end:   "[ \\t]*$"
				beginCaptures:
					1: name: "variable.other.constant.info"
					2: name: "keyword.operator.assignment.key-value.colon.info"
				contentName: "constant.other.local-variable.info"
			}]
		}]


	# Cross-reference
	xref:
		name:  "meta.xref.info"
		begin: "(\\*)([Nn]ote)(?=\\s|$)"
		end:   "(::|\\.|,)|(?=\\x1F)"
		beginCaptures:
			1: name: "punctuation.definition.reference.info"
			2: name: "storage.type.reference.info"
		endCaptures:
			1: name: "punctuation.terminator.reference.info"
		patterns: [{
			name:  "entity.name.tag.topic.info"
			begin: "\\G"
			end:   "(?=:(?::|\\s|$))|(?=\\x1F)"
			patterns: [include: "#escape"]
		},{
			contentName: "entity.name.node.info"
			begin: ":(?!:)[ \\t]*"
			end:   "(?=[ \\t]*[.,\\x1F])"
			beginCaptures:
				0: name: "punctuation.delimiter.separator.colon.info"
		}]
