%YAML 1.2 --- # https://www.sublimetext.com/docs/syntax.html # https://html.spec.whatwg.org/multipage/syntax.html name: HTML scope: text.html.basic version: 2 extends: HTML (Plain).sublime-syntax file_extensions: - html - htm - shtml - xhtml first_line_match: (?i)<(!DOCTYPE\s*)?html ############################################################################### variables: # HTML tags # https://developer.mozilla.org/en-US/docs/Web/HTML/Element # Note: Sections are sorted by expected frequency. html_block_tags: |- (?x: {{html_text_content_tags}} | {{html_section_tags}} | {{html_embedded_tags}} | {{html_web_tags}} | {{html_header_tags}} | {{html_deprecated_block_tags}} ) html_inline_tags: |- (?x: {{html_text_semantic_tags}} | {{html_markup_tags}} | {{html_media_tags}} | {{html_interactive_tags}} | {{html_script_tags}} | {{html_deprecated_inline_tags}} ) # Aggregates structural root notes from # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#main_root # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#document_metadata # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#sectioning_root html_structure_tags: |- (?xi: html | head | body ){{tag_name_break}} # Note: is moved to `html_structure_tags` html_header_tags: |- (?xi: base | link | meta | (?# script | style | ) title ){{tag_name_break}} html_section_tags: |- (?xi: address | article | aside | footer | header | h[1-6] | hgroup | main | nav | section ){{tag_name_break}} html_text_content_tags: |- (?xi: blockquote | cite | dd | dt | dl | div | figcaption | figure | hr | li | ol | p | pre | ul ){{tag_name_break}} html_text_semantic_tags: |- (?xi: a | abbr | b | bdi | bdo | br | code | data | time | dfn | em | i | kbd | mark | q | ruby | rp | rt | s | samp | small | span | strong | sub | sup | u | var | wbr ){{tag_name_break}} html_media_tags: |- (?xi: area | audio | img | map | track | video ){{tag_name_break}} html_embedded_tags: |- (?xi: embed | iframe | object | param | picture | source ){{tag_name_break}} html_script_tags: |- (?xi: canvas | noscript (?# | script ) ){{tag_name_break}} html_markup_tags: |- (?xi: del | ins ){{tag_name_break}} html_table_tags: |- (?xi: caption | col | colgroup | table | tbody | tr | td | tfoot | th | thead ){{tag_name_break}} html_forms_tags: |- (?xi: button | datalist | (?# fieldset | form | ) input | label | legend | meter | optgroup | option | output | progress | select | textarea ){{tag_name_break}} html_interactive_tags: |- (?xi: details | dialog | menu | summary ){{tag_name_break}} html_web_tags: |- (?xi: slot | template ){{tag_name_break}} html_deprecated_block_tags: |- (?xi: applet | bgsound | content | dir | element | frame | frameset | marquee | noembed | noframes | plaintext | xmp ){{tag_name_break}} html_deprecated_inline_tags: |- (?xi: acronym | basefont | big | blink | center | command | font | image | keygen | menuitem | nobr | param | rb | rtc | shadow | spacer | strike | tt ){{tag_name_break}} html_mime_type: |- (?xi: text/html {{mime_type_parameters}}? ) javascript_mime_type: |- (?xi: (?: # default JS MIME types # https://mimesniff.spec.whatwg.org/#javascript-mime-type (?:application|text)/(?:x-)?(?:java|ecma)script | text/javascript1\.[0-5] | text/jscript | text/livescript # non-standard JS MIME types | text/babel ) {{mime_type_parameters}}? ) # A JSON MIME type # is any MIME type whose subtype ends in "+json" # or whose essence is "application/json" or "text/json". # https://mimesniff.spec.whatwg.org/#understanding-mime-types # https://mimesniff.spec.whatwg.org/#json-mime-type json_mime_type: |- (?xi: (?: application/ | text/ | [[:ascii:]]+\+ ) json {{mime_type_parameters}}? ) # A MIME type’s parameters is an ordered map whose keys are ASCII strings and values are strings # limited to HTTP quoted-string token code points. It is initially empty. mime_type_parameters: (?:;.*?) # Embedded script and style syntaxes may be wrapped into html comments for # historical reasons. The following patterns match them, while maintaining # correct boundaries of embedded source scopes. That's required to enable # embedded syntax's comment toggling in the first and last line. # # see: # - https://github.com/sublimehq/Packages/issues/2322 # - https://github.com/sublimehq/sublime_text/issues/4701 script_content_begin: |- (?x: # whitespace followed by opening html comment begin punctuation \s*() \s* )? (?=) \s* $ ) style_content_begin: '{{script_content_begin}}' style_content_end: |- (?x: # optional html comment end punctuation followed by tag (?: (\s*) (-->) \s* )? (?=) \s* $ ) event_attribute_names: |- (?xi: onabort | onautocomplete | onautocompleteerror | onauxclick | onblur | oncancel | oncanplay | oncanplaythrough | onchange | onclick | onclose | oncontextmenu | oncopy | oncuechange | ondblclick | ondrag | ondragend | ondragenter | ondragexit | ondragleave | ondragover | ondragstart | ondrop | ondurationchange | onemptied | onended | onerror | onfocus | onfocusin | onfocusout | oninput | oninvalid | onkeydown | onkeypress | onkeyup | onload | onloadeddata | onloadedmetadata | onloadstart | onmousedown | onmouseenter | onmouseleave | onmousemove | onmouseout | onmouseover | onmouseup | onmousewheel | onpaste | onpause | onplay | onplaying | onprogress | onratechange | onreset | onresize | onscroll | onseeked | onseeking | onselect | onshow | onsort | onstalled | onsubmit | onsuspend | ontimeupdate | ontoggle | onvolumechange | onwaiting ){{attribute_name_break}} ############################################################################### contexts: tag: - include: tag-html - include: tag-other - include: tag-incomplete ###[ HTML TAGS ]############################################################## tag-html: - include: script-tag - include: style-tag - match: (' scope: punctuation.definition.tag.end.html set: script-javascript-content - include: script-common script-javascript-content: - meta_include_prototype: false - match: \s*(( escape_captures: 0: meta.tag.sgml.cdata.html punctuation.definition.tag.end.html - match: '{{script_content_begin}}' captures: 1: comment.block.html punctuation.definition.comment.begin.html pop: 1 # make sure to match only once embed: scope:source.js embed_scope: source.js.embedded.html escape: '{{script_content_end}}' escape_captures: 1: source.js.embedded.html 2: comment.block.html punctuation.definition.comment.end.html 3: source.js.embedded.html 4: comment.block.html punctuation.definition.comment.end.html script-json: - meta_include_prototype: false - meta_scope: meta.tag.script.begin.html - match: '>' scope: punctuation.definition.tag.end.html set: script-json-content - include: script-common script-json-content: - meta_include_prototype: false - match: \s*(( escape_captures: 0: meta.tag.sgml.cdata.html punctuation.definition.tag.end.html - match: '{{script_content_begin}}' captures: 1: comment.block.html punctuation.definition.comment.begin.html pop: 1 # make sure to match only once embed: scope:source.json embed_scope: source.json.embedded.html escape: '{{script_content_end}}' escape_captures: 1: source.json.embedded.html 2: comment.block.html punctuation.definition.comment.end.html 3: source.json.embedded.html 4: comment.block.html punctuation.definition.comment.end.html script-html: - meta_include_prototype: false - meta_scope: meta.tag.script.begin.html - include: tag-end - include: script-common script-other: - meta_scope: meta.tag.script.begin.html - match: '>' scope: punctuation.definition.tag.end.html set: script-close-tag - include: script-common script-common: - include: script-type-attribute - include: tag-attributes - include: tag-end-self-closing script-type-attribute: - match: (?i:type){{attribute_name_break}} scope: meta.attribute-with-value.html entity.other.attribute-name.html set: script-type-attribute-assignment script-type-attribute-assignment: - meta_include_prototype: false - meta_content_scope: meta.tag.script.begin.html meta.attribute-with-value.html - match: = scope: punctuation.separator.key-value.html set: script-type-attribute-value - match: (?=\S) set: script-javascript script-type-attribute-value: - meta_include_prototype: false - meta_scope: meta.tag.script.begin.html meta.attribute-with-value.html - include: script-type-decider script-type-decider: - match: (?={{javascript_mime_type}}{{unquoted_attribute_break}}|'{{javascript_mime_type}}'|"{{javascript_mime_type}}") set: - script-javascript - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=(?i:module{{unquoted_attribute_break}}|'module'|"module")) set: - script-javascript - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=>|''|"") set: - script-javascript - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?={{json_mime_type}}{{unquoted_attribute_break}}|'{{json_mime_type}}'|"{{json_mime_type}}") set: - script-json - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=(?i:{{html_mime_type}}{{unquoted_attribute_break}}|'{{html_mime_type}}'|"{{html_mime_type}}")) set: - script-html - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=\S) set: - script-other - tag-generic-attribute-meta - tag-generic-attribute-value ###[ STYLE TAG ]############################################################## style-tag: - match: (<)((?i:style)){{tag_name_break}} captures: 1: punctuation.definition.tag.begin.html 2: entity.name.tag.style.html push: style-css - match: (' scope: punctuation.definition.tag.end.html set: style-css-content - include: style-common style-css-content: - meta_include_prototype: false - match: \s*(( escape_captures: 0: meta.tag.sgml.cdata.html punctuation.definition.tag.end.html - match: '{{style_content_begin}}' captures: 1: comment.block.html punctuation.definition.comment.begin.html pop: 1 # make sure to match only once embed: scope:source.css embed_scope: source.css.embedded.html escape: '{{style_content_end}}' escape_captures: 1: source.css.embedded.html 2: comment.block.html punctuation.definition.comment.end.html 3: source.css.embedded.html 4: comment.block.html punctuation.definition.comment.end.html style-other: - meta_include_prototype: false - meta_scope: meta.tag.style.begin.html - match: '>' scope: punctuation.definition.tag.end.html set: style-close-tag - include: style-common style-common: - include: style-type-attribute - include: tag-attributes - include: tag-end-self-closing style-type-attribute: - match: (?i:type){{attribute_name_break}} scope: meta.attribute-with-value.html entity.other.attribute-name.html set: style-type-attribute-assignment style-type-attribute-assignment: - meta_include_prototype: false - meta_content_scope: meta.tag.style.begin.html meta.attribute-with-value.html - match: = scope: punctuation.separator.key-value.html set: style-type-attribute-value - match: (?=\S) set: style-css style-type-attribute-value: - meta_include_prototype: false - meta_scope: meta.tag.style.begin.html meta.attribute-with-value.html - include: style-type-decider style-type-decider: - match: (?=(?i:text/css{{unquoted_attribute_break}}|'text/css'|"text/css")) set: - style-css - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=>|''|"") set: - style-css - tag-generic-attribute-meta - tag-generic-attribute-value - match: (?=\S) set: - style-other - tag-generic-attribute-meta - tag-generic-attribute-value ###[ ATTRIBUTES ]############################################################# # This is to prevent breaking syntaxes referencing the old context name tag-stuff: - include: tag-attributes tag-attributes: - include: tag-id-attribute - include: tag-class-attribute - include: tag-style-attribute - include: tag-event-attribute - include: tag-href-attribute - include: tag-generic-attribute tag-attribute-value-separator-double-quoted: - match: (?=[{{ascii_space}}]) push: - clear_scopes: 3 # clear `meta.class-name` or `meta.toc-list.id` - meta_include_prototype: false - meta_scope: meta.string.html string.quoted.double.html - include: else-pop tag-attribute-value-separator-single-quoted: - match: (?=[{{ascii_space}}]) push: - clear_scopes: 3 # clear `meta.class-name` or `meta.toc-list.id` - meta_include_prototype: false - meta_scope: meta.string.html string.quoted.single.html - include: else-pop ###[ CLASS ATTRIBUTE ]######################################################## tag-class-attribute: - match: (?i:class){{attribute_name_break}} scope: entity.other.attribute-name.class.html push: - tag-class-attribute-meta - tag-class-attribute-assignment tag-class-attribute-meta: - meta_include_prototype: false - meta_scope: meta.attribute-with-value.class.html - include: immediately-pop tag-class-attribute-assignment: - meta_include_prototype: false - match: = scope: punctuation.separator.key-value.html set: tag-class-attribute-value - include: else-pop tag-class-attribute-value: - meta_include_prototype: false - match: \" scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html set: tag-class-attribute-value-double-quoted-content - match: \' scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html set: tag-class-attribute-value-single-quoted-content - match: '{{unquoted_attribute_start}}' set: tag-class-attribute-value-unquoted-content - include: else-pop tag-class-attribute-value-double-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.class-name.html meta.string.html string.quoted.double.html - include: strings-double-quoted-end - include: tag-class-attribute-value-quoted-content - include: tag-attribute-value-separator-double-quoted tag-class-attribute-value-single-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.class-name.html meta.string.html string.quoted.single.html - include: strings-single-quoted-end - include: tag-class-attribute-value-quoted-content - include: tag-attribute-value-separator-single-quoted tag-class-attribute-value-quoted-content: - include: tag-class-attribute-value-content tag-class-attribute-value-unquoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.class-name.html meta.string.html string.unquoted.html - include: tag-attribute-value-unquoted-end - include: tag-class-attribute-value-content - include: tag-attribute-value-unquoted-invalid-char tag-class-attribute-value-content: - include: tag-attribute-value-content ###[ EVENT ATTRIBUTE ]######################################################## tag-event-attribute: - match: '{{event_attribute_names}}' scope: entity.other.attribute-name.event.html push: - tag-event-attribute-meta - tag-event-attribute-assignment tag-event-attribute-meta: - meta_include_prototype: false - meta_scope: meta.attribute-with-value.event.html - include: immediately-pop tag-event-attribute-assignment: - meta_include_prototype: false - match: = scope: punctuation.separator.key-value.html set: tag-event-attribute-value - include: else-pop tag-event-attribute-value: - match: \" scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html embed: scope:source.js embed_scope: meta.string.html meta.interpolation.html source.js.embedded.html escape: \" escape_captures: 0: meta.string.html string.quoted.double.html punctuation.definition.string.end.html - match: \' scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html embed: scope:source.js embed_scope: meta.string.html meta.interpolation.html source.js.embedded.html escape: \' escape_captures: 0: meta.string.html string.quoted.single.html punctuation.definition.string.end.html - include: else-pop ###[ HREF ATTRIBUTE ]######################################################### tag-href-attribute: - match: (?i:href|src){{attribute_name_break}} scope: entity.other.attribute-name.href.html push: - tag-href-attributes-meta - tag-href-attributes-assignment tag-href-attributes-meta: - meta_include_prototype: false - meta_scope: meta.attribute-with-value.href.html - include: immediately-pop tag-href-attributes-assignment: - meta_include_prototype: false - match: = scope: punctuation.separator.key-value.html set: tag-href-attribute-value - include: else-pop tag-href-attribute-value: - meta_include_prototype: false - match: \" scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html set: tag-href-attribute-value-double-quoted-content - match: \' scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html set: tag-href-attribute-value-single-quoted-content - match: '{{unquoted_attribute_start}}' set: tag-href-attribute-value-unquoted-content - include: else-pop tag-href-attribute-value-double-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.path.url.html meta.string.html string.quoted.double.html - include: strings-double-quoted-end - include: tag-href-attribute-value-quoted-content tag-href-attribute-value-single-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.path.url.html meta.string.html string.quoted.single.html - include: strings-single-quoted-end - include: tag-href-attribute-value-quoted-content tag-href-attribute-value-quoted-content: - include: tag-href-attribute-value-content tag-href-attribute-value-unquoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.path.url.html meta.string.html string.unquoted.html - include: tag-attribute-value-unquoted-end - include: tag-href-attribute-value-content - include: tag-attribute-value-unquoted-invalid-char tag-href-attribute-value-content: - include: tag-attribute-value-content - match: (%)\h{2} scope: constant.character.escape.url.html captures: 1: punctuation.definition.escape.html - match: '[/&?#]|://' scope: punctuation.separator.path.html ###[ ID ATTRIBUTE ]########################################################### tag-id-attribute: - match: (?i:id){{attribute_name_break}} scope: entity.other.attribute-name.id.html push: - tag-id-attribute-meta - tag-id-attribute-assignment tag-id-attribute-meta: - meta_include_prototype: false - meta_scope: meta.attribute-with-value.id.html - include: immediately-pop tag-id-attribute-assignment: - meta_include_prototype: false - match: = scope: punctuation.separator.key-value.html set: tag-id-attribute-value - include: else-pop tag-id-attribute-value: - meta_include_prototype: false - match: \" scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html set: tag-id-attribute-value-double-quoted-content - match: \' scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html set: tag-id-attribute-value-single-quoted-content - match: '{{unquoted_attribute_start}}' set: tag-id-attribute-value-unquoted-content - include: else-pop tag-id-attribute-value-double-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.toc-list.id.html meta.string.html string.quoted.double.html - include: strings-double-quoted-end - include: tag-id-attribute-value-quoted-content - include: tag-attribute-value-separator-double-quoted tag-id-attribute-value-single-quoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.toc-list.id.html meta.string.html string.quoted.single.html - include: strings-single-quoted-end - include: tag-id-attribute-value-quoted-content - include: tag-attribute-value-separator-single-quoted tag-id-attribute-value-quoted-content: - include: tag-id-attribute-value-content tag-id-attribute-value-unquoted-content: # See HTML (Plain).sublime-syntax for why prototype is excluded - meta_include_prototype: false - meta_content_scope: meta.toc-list.id.html meta.string.html string.unquoted.html - include: tag-attribute-value-unquoted-end - include: tag-id-attribute-value-content - include: tag-attribute-value-unquoted-invalid-char tag-id-attribute-value-content: - include: tag-attribute-value-content ###[ STYLE ATTRIBUTE ]######################################################## tag-style-attribute: - match: (?i:style){{attribute_name_break}} scope: entity.other.attribute-name.style.html push: - tag-style-attribute-meta - tag-style-attribute-assignment tag-style-attribute-meta: - meta_include_prototype: false - meta_scope: meta.attribute-with-value.style.html - include: immediately-pop tag-style-attribute-assignment: - meta_include_prototype: false - match: = scope: punctuation.separator.key-value.html set: tag-style-attribute-value - include: else-pop tag-style-attribute-value: - match: \" scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html embed: scope:source.css#rule-list-body embed_scope: meta.string.html meta.interpolation.html source.css.embedded.html escape: \" escape_captures: 0: meta.string.html string.quoted.double.html punctuation.definition.string.end.html - match: \' scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html embed: scope:source.css#rule-list-body embed_scope: meta.string.html meta.interpolation.html source.css.embedded.html escape: \' escape_captures: 0: meta.string.html string.quoted.single.html punctuation.definition.string.end.html - include: else-pop