diff --git a/grammars/textmate/python-markdown/src/python-markdown.tmLanguage.yml b/grammars/textmate/python-markdown/src/python-markdown.tmLanguage.yml index 152e3f3..fd8d017 100644 --- a/grammars/textmate/python-markdown/src/python-markdown.tmLanguage.yml +++ b/grammars/textmate/python-markdown/src/python-markdown.tmLanguage.yml @@ -26,6 +26,7 @@ scopeName: text.html.markdown.python patterns: - include: "#front_matter" + - include: "#snippet" - include: "#flow" repository: @@ -60,8 +61,135 @@ repository: - include: "#heading_atx" - include: "#thematic_break" - include: "#block_quote" + - include: "#list_item" - include: "#paragraph" + snippet: + patterns: + - name: meta.snippet.section.markdown + match: >- + (?:^|\G)(.*?)(?- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(")((?:\\"|[^"\r\n])+?)((?::-?[0-9]*){1,2}(?:(?:,(?=[-0-9:])-?[0-9]*)(?::-?[0-9]*)?)*)(")$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.double.snippet.markdown } + 6: { name: constant.numeric.snippet-selector.line.markdown } + 7: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.inline.markdown + match: >- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(")((?:\\"|[^"\r\n])+?)(:[a-z][-_0-9a-z]*)(")$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.double.snippet.markdown } + 6: { name: entity.name.section.snippet-selector.markdown } + 7: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.inline.markdown + match: >- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(")((?:\\"|[^"\r\n])+?)(")$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.double.snippet.markdown } + 6: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.inline.markdown + match: >- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(')((?:\\'|[^'\r\n])+?)((?::-?[0-9]*){1,2}(?:(?:,(?=[-0-9:])-?[0-9]*)(?::-?[0-9]*)?)*)(')$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.single.snippet.markdown } + 6: { name: constant.numeric.snippet-selector.line.markdown } + 7: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.inline.markdown + match: >- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(')((?:\\'|[^'\r\n])+?)(:[a-z][-_0-9a-z]*)(')$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.single.snippet.markdown } + 6: { name: entity.name.section.snippet-selector.markdown } + 7: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.inline.markdown + match: >- + (?:^|\G)([ \t]*)(-{1,}8<-{1,})([ \t]+)(')((?:\\'|[^'\r\n])+?)(')$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + 3: { name: comment.snippet.separator.markdown } + 4: { name: punctuation.definition.string.begin.markdown } + 5: { name: string.quoted.single.snippet.markdown } + 6: { name: punctuation.definition.string.end.markdown } + - name: meta.snippet.block.markdown + begin: (?:^|\G)([ \t]*)(-{1,}8<-{1,})(?![ \t])$ + beginCaptures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + end: (?:^|\G)([ \t]*)(-{1,}8<-{1,})(?![ \t])$ + endCaptures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.snippet.markdown } + patterns: + - match: (?:^|\G)([ \t]*)(;[^\r\n]*?)$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: comment.line.snippet.markdown } + - match: >- + (?:^|\G)([ \t]*)(?=[^;\s\r\n])(.*?)(:[-0-9:,]+)([ \t]*)$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: string.unquoted.snippet.markdown } + 3: { name: constant.numeric.snippet-selector.line.markdown } + 4: { name: punctuation.whitespace.trailing.markdown } + - match: >- + (?:^|\G)([ \t]*)(?=[^;\s\r\n])(.*?)(:[a-z][-_0-9a-z]*)([ \t]*)$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: string.unquoted.snippet.markdown } + 3: { name: entity.name.section.snippet-selector.markdown } + 4: { name: punctuation.whitespace.trailing.markdown } + - match: (?:^|\G)([ \t]*)(?=[^;\s\r\n])([^\r\n]*?)([ \t]*)$ + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: string.unquoted.snippet.markdown } + 3: { name: punctuation.whitespace.trailing.markdown } + - match: (?:^|\G)([ \t]+)$ + captures: + 1: { name: punctuation.whitespace.markdown } + table: patterns: - name: markup.table.markdown @@ -386,6 +514,37 @@ repository: - match: (?:^|\G)[ \t]*([\*\-\_])([ ]{0,2}\1){2,}[ \t]*$\n? name: meta.thematic-break.markdown + list_item: + patterns: + - match: >- + (?:^|\G)([ \t]*)([*+-])([ ]+)(\[(?: |x|X)\])([ \t]+) + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.list.unordered.markdown } + 3: { name: punctuation.whitespace.markdown } + 4: { name: keyword.control.task-checkbox.markdown } + 5: { name: punctuation.whitespace.markdown } + - match: >- + (?:^|\G)([ \t]*)(\d+\.)([ ]+)(\[(?: |x|X)\])([ \t]+) + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.list.ordered.markdown } + 3: { name: punctuation.whitespace.markdown } + 4: { name: keyword.control.task-checkbox.markdown } + 5: { name: punctuation.whitespace.markdown } + - match: >- + (?:^|\G)([ \t]*)([*+-])([ ]+)(?=\S) + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.list.unordered.markdown } + 3: { name: punctuation.whitespace.markdown } + - match: >- + (?:^|\G)([ \t]*)(\d+\.)([ ]+)(?=\S) + captures: + 1: { name: punctuation.whitespace.leading.markdown } + 2: { name: keyword.control.list.ordered.markdown } + 3: { name: punctuation.whitespace.markdown } + block_quote: patterns: - name: meta.block.quote.markdown @@ -404,13 +563,16 @@ repository: end: (?=$) patterns: - include: "#inline_flow" - - include: text.html.derivative # - include: "#heading_setext" inline_flow: patterns: - - include: "#escape" - include: "#inline_code" + - include: "#inline_math" + - include: "#escape" + - include: "#emoji" + - include: "#keys" + - include: "#smart_symbol" - include: "#mark" - include: "#emphasis" - include: "#image" @@ -419,11 +581,7 @@ repository: - include: "#link" - include: "#link_reference" - include: "#autolink" - - escape: - patterns: - - match: \\. - name: constant.character.escape.markdown + - include: text.html.derivative inline_code: patterns: @@ -493,6 +651,86 @@ repository: - include: "#attribute" 4: { name: punctuation.definition.attribute-list.end.markdown } + inline_math: + patterns: + - name: markup.math.inline.markdown + begin: (\$)(?=[^\s$]) + beginCaptures: + 1: { name: punctuation.definition.math.begin.markdown } + contentName: meta.embedded.inline.latex + end: (?- + (\+{2})(?=(?:[\p{L}\p{N}_-]+|"(?:\\.|[^"\\+])+"|'(?:\\.|[^'\\+])+')(?:\+(?:[\p{L}\p{N}_-]+|"(?:\\.|[^"\\+])+"|'(?:\\.|[^'\\+])+'))*\+{2}) + beginCaptures: + 1: { name: punctuation.definition.keys.begin.markdown } + end: \+{2} + endCaptures: + 0: { name: punctuation.definition.keys.end.markdown } + patterns: + - match: \+ + name: punctuation.separator.key.markdown + - match: (")((?:\\.|[^"\\+])+)(") + captures: + 1: { name: punctuation.definition.string.begin.key.markdown } + 2: { name: constant.other.key.markdown } + 3: { name: punctuation.definition.string.end.key.markdown } + - match: (')((?:\\.|[^'\\+])+)(') + captures: + 1: { name: punctuation.definition.string.begin.key.markdown } + 2: { name: constant.other.key.markdown } + 3: { name: punctuation.definition.string.end.key.markdown } + - match: '[\p{L}\p{N}_-]+' + name: constant.other.key.markdown + + smart_symbol: + patterns: + - name: constant.other.smart-symbol.markdown + match: \(tm\)|\(c\)|\(r\) + - name: constant.other.smart-symbol.markdown + match: \+/-|=/= + - name: constant.other.smart-symbol.markdown + match: <-->|(?|<--(?!-) + - name: constant.other.smart-symbol.markdown + match: (?- + (?- + (?", + "tokens": [ + { + "text": "-->", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_both": [ + { + "line": "<-->", + "tokens": [ + { + "text": "<-->", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "care_of": { + "accept_plain": [ + { + "line": "c/o", + "tokens": [ + { + "text": "c/o", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "fraction": { + "accept_half": [ + { + "line": "1/2", + "tokens": [ + { + "text": "1/2", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_five_eighths": [ + { + "line": "5/8", + "tokens": [ + { + "text": "5/8", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "ordinal": { + "accept_first": [ + { + "line": "1st", + "tokens": [ + { + "text": "1st", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_eleventh": [ + { + "line": "11th", + "tokens": [ + { + "text": "11th", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_twelfth": [ + { + "line": "12th", + "tokens": [ + { + "text": "12th", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_thirty_second": [ + { + "line": "32nd", + "tokens": [ + { + "text": "32nd", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_hundred_third": [ + { + "line": "103rd", + "tokens": [ + { + "text": "103rd", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "position": { + "accept_before_text": [ + { + "line": "(c) Text", + "tokens": [ + { + "text": "(c)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " Text", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_after_text": [ + { + "line": "Text (c)", + "tokens": [ + { + "text": "Text ", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "(c)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_multiple": [ + { + "line": "(c) +/- 1/2", + "tokens": [ + { + "text": "(c)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "+/-", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "1/2", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "nesting": { + "accept_inside_emphasis": [ + { + "line": "*(c)*", + "tokens": [ + { + "text": "*", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "markup.italic.markdown", + "punctuation.definition.italic.markdown" + ] + }, + { + "text": "(c)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "markup.italic.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": "*", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "markup.italic.markdown", + "punctuation.definition.italic.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_inside_link": [ + { + "line": "[(c)](href)", + "tokens": [ + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "punctuation.definition.link.begin.markdown" + ] + }, + { + "text": "(c)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "string.other.link.title.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "punctuation.definition.link.end.markdown" + ] + }, + { + "text": "(", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "punctuation.definition.link.begin.markdown" + ] + }, + { + "text": "href", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "markup.underline.link.markdown" + ] + }, + { + "text": ")", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "meta.link.inline.markdown", + "punctuation.definition.link.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "rejection": { + "reject_escaped_copyright": [ + { + "line": "\\(c)", + "tokens": [ + { + "text": "\\(", + "scopes": [ + "text.html.markdown.python", + "constant.character.escape.markdown" + ] + }, + { + "text": "c)", + "scopes": [ + "text.html.markdown.python" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_uppercase_trademark": [ + { + "line": "(TM)", + "tokens": [ + { + "text": "(TM)", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_uppercase_care_of": [ + { + "line": "C/O", + "tokens": [ + { + "text": "C/O", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_arrow_right_tail": [ + { + "line": "---", + "tokens": [ + { + "text": "---", + "scopes": [ + "text.html.markdown.python", + "punctuation.definition.begin.frontmatter" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_arrow_left_tail": [ + { + "line": "<---", + "tokens": [ + { + "text": "<---", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_wrong_ordinal_eleventh_second": [ + { + "line": "11nd", + "tokens": [ + { + "text": "11nd", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_wrong_ordinal_tenth": [ + { + "line": "10st", + "tokens": [ + { + "text": "10st", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_wrong_ordinal_twenty_second": [ + { + "line": "22th", + "tokens": [ + { + "text": "22th", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_leading_zero_ordinal": [ + { + "line": "01st", + "tokens": [ + { + "text": "01st", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_fraction_digit_before": [ + { + "line": "11/2", + "tokens": [ + { + "text": "11/2", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_fraction_digit_after": [ + { + "line": "1/22", + "tokens": [ + { + "text": "1/22", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_care_of_before_underscore": [ + { + "line": "c/o_", + "tokens": [ + { + "text": "c/o_", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + } +} diff --git a/grammars/textmate/python-markdown/test/snapshots/preprocess/snippet.scopes.json b/grammars/textmate/python-markdown/test/snapshots/preprocess/snippet.scopes.json new file mode 100644 index 0000000..6be6d01 --- /dev/null +++ b/grammars/textmate/python-markdown/test/snapshots/preprocess/snippet.scopes.json @@ -0,0 +1,1140 @@ +{ + "inline": { + "accept_double_quoted": [ + { + "line": "--8<-- \"index.md\"", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.begin.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "string.quoted.double.snippet.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_single_quoted": [ + { + "line": "---8<--- 'index.md'", + "tokens": [ + { + "text": "---8<---", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "'", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.begin.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "string.quoted.single.snippet.markdown" + ] + }, + { + "text": "'", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_indented": [ + { + "line": " --8<-- \"index.md\"", + "tokens": [ + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.whitespace.leading.markdown" + ] + }, + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.begin.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "string.quoted.double.snippet.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_section_selector": [ + { + "line": "--8<-- \"index.md:example\"", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.begin.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "string.quoted.double.snippet.markdown" + ] + }, + { + "text": ":example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "entity.name.section.snippet-selector.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_line_selector": [ + { + "line": "--8<-- \"index.md:1:3,5:7\"", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.begin.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "string.quoted.double.snippet.markdown" + ] + }, + { + "text": ":1:3,5:7", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "constant.numeric.snippet-selector.line.markdown" + ] + }, + { + "text": "\"", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.inline.markdown", + "punctuation.definition.string.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_escaped": [ + { + "line": ";--8<-- \"index.md\"", + "tokens": [ + { + "text": ";--8", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "<--", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " \"index.md\"", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_unquoted": [ + { + "line": "--8<-- index.md", + "tokens": [ + { + "text": "--8", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "<--", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " index.md", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_trailing_space": [ + { + "line": "--8<-- \"index.md\" ", + "tokens": [ + { + "text": "--8", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + }, + { + "text": "<--", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown", + "constant.other.smart-symbol.markdown" + ] + }, + { + "text": " \"index.md\" ", + "scopes": [ + "text.html.markdown.python", + "meta.paragraph.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "block": { + "accept_plain": [ + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "index.md", + "tokens": [ + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + } + ] + }, + { + "line": "about.md", + "tokens": [ + { + "text": "about.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + } + ] + }, + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_blank_lines": [ + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + }, + { + "line": " ", + "tokens": [ + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "punctuation.whitespace.markdown" + ] + } + ] + }, + { + "line": "index.md", + "tokens": [ + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + } + ] + }, + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_selectors": [ + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "index.md:1:3", + "tokens": [ + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + }, + { + "text": ":1:3", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "constant.numeric.snippet-selector.line.markdown" + ] + } + ] + }, + { + "line": "about.md:section", + "tokens": [ + { + "text": "about.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + }, + { + "text": ":section", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "entity.name.section.snippet-selector.markdown" + ] + } + ] + }, + { + "line": "https://example.com/", + "tokens": [ + { + "text": "https://example.com/", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + } + ] + }, + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_line_selector_split": [ + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "snippet.md:1:3", + "tokens": [ + { + "text": "snippet.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + }, + { + "text": ":1:3", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "constant.numeric.snippet-selector.line.markdown" + ] + } + ] + }, + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_indented": [ + { + "line": " --8<--", + "tokens": [ + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "punctuation.whitespace.leading.markdown" + ] + }, + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": " index.md ", + "tokens": [ + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "punctuation.whitespace.leading.markdown" + ] + }, + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "punctuation.whitespace.trailing.markdown" + ] + } + ] + }, + { + "line": " --8<--", + "tokens": [ + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "punctuation.whitespace.leading.markdown" + ] + }, + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_comment_line": [ + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "; comment", + "tokens": [ + { + "text": "; comment", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "comment.line.snippet.markdown" + ] + } + ] + }, + { + "line": "index.md", + "tokens": [ + { + "text": "index.md", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "string.unquoted.snippet.markdown" + ] + } + ] + }, + { + "line": "--8<--", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.block.markdown", + "keyword.control.snippet.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + }, + "section": { + "accept_start": [ + { + "line": "--8<-- [start:example]", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.begin.markdown" + ] + }, + { + "text": "start", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.section.markdown" + ] + }, + { + "text": ":", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.separator.snippet-section.markdown" + ] + }, + { + "text": "example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "entity.name.section.snippet.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_end": [ + { + "line": "--8<-- [end:example]", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.begin.markdown" + ] + }, + { + "text": "end", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.section.markdown" + ] + }, + { + "text": ":", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.separator.snippet-section.markdown" + ] + }, + { + "text": "example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "entity.name.section.snippet.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_with_prefix_and_suffix": [ + { + "line": "prefix --8<-- [start:example] suffix", + "tokens": [ + { + "text": "prefix ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "meta.snippet.section.prefix.markdown" + ] + }, + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.begin.markdown" + ] + }, + { + "text": "start", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.section.markdown" + ] + }, + { + "text": ":", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.separator.snippet-section.markdown" + ] + }, + { + "text": "example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "entity.name.section.snippet.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.end.markdown" + ] + }, + { + "text": " suffix", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "meta.snippet.section.suffix.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "accept_uppercase_keyword": [ + { + "line": "--8<-- [START:example]", + "tokens": [ + { + "text": "--8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.begin.markdown" + ] + }, + { + "text": "START", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.section.markdown" + ] + }, + { + "text": ":", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.separator.snippet-section.markdown" + ] + }, + { + "text": "example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "entity.name.section.snippet.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ], + "reject_escaped": [ + { + "line": ";--8<-- [start:example]", + "tokens": [ + { + "text": ";-", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "meta.snippet.section.prefix.markdown" + ] + }, + { + "text": "-8<--", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.markdown" + ] + }, + { + "text": " ", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "comment.snippet.separator.markdown" + ] + }, + { + "text": "[", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.begin.markdown" + ] + }, + { + "text": "start", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "keyword.control.snippet.section.markdown" + ] + }, + { + "text": ":", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.separator.snippet-section.markdown" + ] + }, + { + "text": "example", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "entity.name.section.snippet.markdown" + ] + }, + { + "text": "]", + "scopes": [ + "text.html.markdown.python", + "meta.snippet.section.markdown", + "punctuation.definition.snippet-section.end.markdown" + ] + } + ] + }, + { + "line": "", + "tokens": [] + } + ] + } +} diff --git a/grammars/textmate/python-markdown/test/specs/flow/list.md b/grammars/textmate/python-markdown/test/specs/flow/list.md new file mode 100644 index 0000000..9b5111b --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/flow/list.md @@ -0,0 +1,101 @@ +## marker + +### accept_unordered_dash + +``` md +- Item +``` + +### accept_unordered_asterisk + +``` md +* Item +``` + +### accept_unordered_plus + +``` md ++ Item +``` + +### accept_ordered + +``` md +1. Item +``` + +### accept_with_3_leading_spaces + +``` md + - Item +``` + +## task + +### accept_unchecked + +``` md +- [ ] Task +``` + +### accept_checked_uppercase + +``` md +* [X] Task +``` + +### accept_ordered + +``` md +1. [ ] Task +``` + +### accept_tab_after_checkbox + +``` md +- [ ] Task +``` + +### accept_2_spaces_after_checkbox + +``` md +- [ ] Task +``` + +### accept_checkbox_without_text + +``` md +- [ ] +``` + +## rejection + +### reject_marker_missing_space + +``` md +-Item +``` + +### reject_checkbox_only + +``` md +- [ ] +``` + +### reject_checkbox_invalid_marker + +``` md +- [y] Task +``` + +### reject_checkbox_without_space_after + +``` md +- [x]Task +``` + +### accept_with_4_leading_spaces + +``` md + - Item +``` diff --git a/grammars/textmate/python-markdown/test/specs/inline_flow/emoji.md b/grammars/textmate/python-markdown/test/specs/inline_flow/emoji.md new file mode 100644 index 0000000..0e1c7d5 --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/inline_flow/emoji.md @@ -0,0 +1,103 @@ +## name + +### accept_plain + +``` md +:smile: +``` + +### accept_with_plus + +``` md +:+1: +``` + +### accept_with_dash + +``` md +:-1: +``` + +### accept_with_underscore + +``` md +:foo_bar: +``` + +### accept_icon_name + +``` md +:material-home: +``` + +### accept_icon_name_with_digits + +``` md +:octicons-arrow-left-16: +``` + +### accept_unicode + +``` md +:é: +``` + +## position + +### accept_before_text + +``` md +:smile: Text +``` + +### accept_after_text + +``` md +Text :smile: +``` + +### accept_multiple + +``` md +:smile::heart: +``` + +### accept_inside_word + +``` md +Text:smile:Text +``` + +## nesting + +### accept_inside_emphasis + +``` md +*:smile:* +``` + +### accept_inside_strong + +``` md +**:smile:** +``` + +### accept_inside_link + +``` md +[:smile:](href) +``` + +## rejection + +### reject_empty + +``` md +:: +``` + +### reject_unterminated + +``` md +:smile +``` diff --git a/grammars/textmate/python-markdown/test/specs/inline_flow/keys.md b/grammars/textmate/python-markdown/test/specs/inline_flow/keys.md new file mode 100644 index 0000000..49daea6 --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/inline_flow/keys.md @@ -0,0 +1,143 @@ +## name + +### accept_named_key + +``` md +++enter++ +``` + +### accept_key_alias + +``` md +++pg-up++ +``` + +### accept_function_key + +``` md +++f12++ +``` + +### accept_unicode_key + +``` md +++é++ +``` + +## sequence + +### accept_key_sequence + +``` md +++ctrl+alt+delete++ +``` + +### accept_key_name_with_side + +``` md +++left-control++ +``` + +### accept_key_name_with_side_in_sequence + +``` md +++ctrl+left-control++ +``` + +## quoted + +### accept_quoted_key + +``` md +++"enter delete"++ +``` + +### accept_single_quoted_key + +``` md +++'enter delete'++ +``` + +### accept_escaped_quoted_key + +``` md +++"control\"alt"++ +``` + +### accept_escaped_plus_in_quoted_key + +``` md +++"control\+alt"++ +``` + +### accept_mixed_named_and_quoted_keys + +``` md +++ctrl+"alt delete"++ +``` + +## position + +### accept_before_text + +``` md +++enter++ Text +``` + +### accept_after_text + +``` md +Text ++enter++ +``` + +### accept_multiple + +``` md +++ctrl++ ++alt++ +``` + +## nesting + +### accept_inside_emphasis + +``` md +*++enter++* +``` + +### accept_inside_link + +``` md +[++enter++](href) +``` + +## rejection + +### reject_blank_key + +``` md +++++ +``` + +### reject_unterminated_key + +``` md +++enter +``` + +### reject_escaped_key + +``` md +\++enter++ +``` + +### reject_unescaped_plus_in_quoted_key + +``` md +++"control+alt"++ +``` + +### reject_empty_quoted_key + +``` md ++""++ +``` diff --git a/grammars/textmate/python-markdown/test/specs/inline_flow/math.md b/grammars/textmate/python-markdown/test/specs/inline_flow/math.md new file mode 100644 index 0000000..44b6c5c --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/inline_flow/math.md @@ -0,0 +1,148 @@ +## marker + +### accept_dollar + +``` md +$Math$ +``` + +### accept_dollar_with_parentheses + +``` md +$(Math)$ +``` + +### accept_dollar_with_escaped_dollar + +``` md +$Math \$ Text$ +``` + +### accept_dollar_with_closing_marker + +``` md +$Math $ Text$ +``` + +### accept_dollar_with_trailing_marker + +``` md +$Math$$ +``` + +### accept_dollar_with_leading_marker + +``` md +$$Math$ +``` + +### accept_round + +``` md +\(Math\) +``` + +### accept_round_with_escaped_parentheses + +``` md +\(Math \\) Text\) +``` + +## position + +### accept_before_text + +``` md +$Math$ Text +``` + +### accept_after_text + +``` md +Text $Math$ +``` + +### accept_inside_emphasis + +``` md +*$Math$* +``` + +### accept_inside_link + +``` md +[$Math$](href) +``` + +## line_ending + +### accept_dollar_with_line_ending + +``` md +$Math +Text$ +``` + +### accept_round_with_line_ending + +``` md +\(Math +Text\) +``` + +## rejection + +### reject_dollar_with_opening_space + +``` md +$ Math$ +``` + +### reject_dollar_with_opening_tab + +``` md +$ Math$ +``` + +### reject_dollar_with_closing_space + +``` md +$Math $ +``` + +### reject_dollar_with_closing_tab + +``` md +$Math $ +``` + +### reject_dollar_with_closing_line_ending + +``` md +$Math +$ +``` + +### reject_dollar_unterminated + +``` md +$Math +``` + +### reject_dollar_with_escaped_opening + +``` md +\$Math$ +``` + +### reject_round_unterminated + +``` md +\(Math +``` + +### reject_square_block_math + +``` md +\[Math\] +``` diff --git a/grammars/textmate/python-markdown/test/specs/inline_flow/smart_symbol.md b/grammars/textmate/python-markdown/test/specs/inline_flow/smart_symbol.md new file mode 100644 index 0000000..f9d4009 --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/inline_flow/smart_symbol.md @@ -0,0 +1,215 @@ +## parenthesized + +### accept_trademark + +``` md +(tm) +``` + +### accept_copyright + +``` md +(c) +``` + +### accept_registered + +``` md +(r) +``` + +## operator + +### accept_plusminus + +``` md ++/- +``` + +### accept_notequal + +``` md +=/= +``` + +## arrow + +### accept_left + +``` md +<-- +``` + +### accept_right + +``` md +--> +``` + +### accept_both + +``` md +<--> +``` + +## care_of + +### accept_plain + +``` md +c/o +``` + +## fraction + +### accept_half + +``` md +1/2 +``` + +### accept_five_eighths + +``` md +5/8 +``` + +## ordinal + +### accept_first + +``` md +1st +``` + +### accept_eleventh + +``` md +11th +``` + +### accept_twelfth + +``` md +12th +``` + +### accept_thirty_second + +``` md +32nd +``` + +### accept_hundred_third + +``` md +103rd +``` + +## position + +### accept_before_text + +``` md +(c) Text +``` + +### accept_after_text + +``` md +Text (c) +``` + +### accept_multiple + +``` md +(c) +/- 1/2 +``` + +## nesting + +### accept_inside_emphasis + +``` md +*(c)* +``` + +### accept_inside_link + +``` md +[(c)](href) +``` + +## rejection + +### reject_escaped_copyright + +``` md +\(c) +``` + +### reject_uppercase_trademark + +``` md +(TM) +``` + +### reject_uppercase_care_of + +``` md +C/O +``` + +### reject_arrow_right_tail + +``` md +--- +``` + +### reject_arrow_left_tail + +``` md +<--- +``` + +### reject_wrong_ordinal_eleventh_second + +``` md +11nd +``` + +### reject_wrong_ordinal_tenth + +``` md +10st +``` + +### reject_wrong_ordinal_twenty_second + +``` md +22th +``` + +### reject_leading_zero_ordinal + +``` md +01st +``` + +### reject_fraction_digit_before + +``` md +11/2 +``` + +### reject_fraction_digit_after + +``` md +1/22 +``` + +### reject_care_of_before_underscore + +``` md +c/o_ +``` diff --git a/grammars/textmate/python-markdown/test/specs/preprocess/snippet.md b/grammars/textmate/python-markdown/test/specs/preprocess/snippet.md new file mode 100644 index 0000000..9a7869e --- /dev/null +++ b/grammars/textmate/python-markdown/test/specs/preprocess/snippet.md @@ -0,0 +1,137 @@ +## inline + +### accept_double_quoted + +``` md +--8<-- "index.md" +``` + +### accept_single_quoted + +``` md +---8<--- 'index.md' +``` + +### accept_indented + +``` md + --8<-- "index.md" +``` + +### accept_section_selector + +``` md +--8<-- "index.md:example" +``` + +### accept_line_selector + +``` md +--8<-- "index.md:1:3,5:7" +``` + +### reject_escaped + +``` md +;--8<-- "index.md" +``` + +### reject_unquoted + +``` md +--8<-- index.md +``` + +### reject_trailing_space + +``` md +--8<-- "index.md" +``` + +## block + +### accept_plain + +``` md +--8<-- +index.md +about.md +--8<-- +``` + +### accept_blank_lines + +``` md +--8<-- + + +index.md +--8<-- +``` + +### accept_selectors + +``` md +--8<-- +index.md:1:3 +about.md:section +https://example.com/ +--8<-- +``` + +### accept_line_selector_split + +``` md +--8<-- +snippet.md:1:3 +--8<-- +``` + +### accept_indented + +``` md + --8<-- + index.md + --8<-- +``` + +### accept_comment_line + +``` md +--8<-- +; comment +index.md +--8<-- +``` + +## section + +### accept_start + +``` md +--8<-- [start:example] +``` + +### accept_end + +``` md +--8<-- [end:example] +``` + +### accept_with_prefix_and_suffix + +``` md +prefix --8<-- [start:example] suffix +``` + +### accept_uppercase_keyword + +``` md +--8<-- [START:example] +``` + +### reject_escaped + +``` md +;--8<-- [start:example] +``` diff --git a/integrations/code/src/extension.ts b/integrations/code/src/extension.ts index 5e3eeb2..8e06e86 100644 --- a/integrations/code/src/extension.ts +++ b/integrations/code/src/extension.ts @@ -24,9 +24,9 @@ import type { ExtensionContext } from "vscode"; import type { LanguageClient } from "vscode-languageclient/node"; import { registerCommands } from "./commands"; -import { promptFileAssociation } from "./extension/association"; import { createLanguageClient } from "./extension/client"; import { Context } from "./extension/context"; +import { activateProjectMarkdown } from "./extension/project"; import { getStudio } from "./extension/studio"; /* ---------------------------------------------------------------------------- @@ -49,14 +49,14 @@ let client: LanguageClient | undefined; */ export async function activate(extension: ExtensionContext): Promise { const context = new Context(extension); + void activateProjectMarkdown(extension, context); + + // Obtain Zensical studio configuration const studio = await getStudio(context); if (typeof studio === "undefined") { return; } - // Prompt user to associate Markdown files with Zensical Studio - await promptFileAssociation(context); - // Register commands registerCommands(extension); diff --git a/integrations/code/src/extension/association.ts b/integrations/code/src/extension/association.ts deleted file mode 100644 index 9e5244b..0000000 --- a/integrations/code/src/extension/association.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2026 Zensical and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -import * as vscode from "vscode"; -import { basename } from "node:path"; - -import type { Context } from "./context"; - -/* ---------------------------------------------------------------------------- - * Types - * ------------------------------------------------------------------------- */ - -/** - * Project type. - */ -type Project = "MkDocs" | "Zensical"; - -/* ---------------------------------------------------------------------------- - * Functions - * ------------------------------------------------------------------------- */ - -/** - * Prompt to associate Markdown files with Python Markdown. - * - * @param context - Extension context - */ -export async function promptFileAssociation(context: Context): Promise { - const folders = vscode.workspace.workspaceFolders ?? []; - if (folders.length !== 1) { - return; - } - - // If we have a single workspace folder, check if it contains a project - const [folder] = folders; - const project = await findProject(folder); - if (typeof project === "undefined") { - return; - } - - // Check, if we already prompted the user for this workspace - const key = `association:${folder.uri.toString()}`; - if (context.getState(key) === true) { - return; - } - - // Check, if we already have an association for Markdown files to the - // Python Markdown grammar in this workspace, and if so, skip - const config = vscode.workspace.getConfiguration("files"); - const effective = config.get>("associations") ?? {}; - if (effective["*.md"] === "python-markdown") { - context.setState(key, true); - return; - } - - // Prompt the user to associate Markdown files with Python Markdown - const result = await vscode.window.showInformationMessage( - `Detected ${project} project. ` + - `Associate Markdown files in '${folder.name}' with Python Markdown?`, - "Yes", - "Not now", - ); - - // If the user dismissed the prompt, don't ask again - if (typeof result === "undefined") { - return; - } - - if (result !== "Yes") { - context.setState(key, true); - return; - } - - // Update file associations for this workspace - const associations = - config.inspect>("associations")?.workspaceValue ?? - {}; - try { - await config.update( - "associations", - { - ...associations, - "*.md": "python-markdown", - }, - vscode.ConfigurationTarget.Workspace, - ); - context.setState(key, true); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - context.log( - `Failed to update file associations for '${folder.name}': ${message}`, - ); - } -} - -/* ---------------------------------------------------------------------------- - * Helper functions - * ------------------------------------------------------------------------- */ - -/** - * Return the first matching project type in a workspace folder. - * - * @param folder - Workspace folder - * - * @returns Project type or nothing - */ -async function findProject( - folder: vscode.WorkspaceFolder, -): Promise { - const [match] = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, "**/{mkdocs.yml,zensical.toml}"), - null, - 1, - ); - - // Return the project type based on the matching file name - switch (basename(match?.fsPath ?? "")) { - case "zensical.toml": - return "Zensical"; - case "mkdocs.yml": - return "MkDocs"; - } - - // No matching project type found - return; -} diff --git a/integrations/code/src/extension/project.ts b/integrations/code/src/extension/project.ts new file mode 100644 index 0000000..438ac7d --- /dev/null +++ b/integrations/code/src/extension/project.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2026 Zensical and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +import * as vscode from "vscode"; +import { basename, extname } from "node:path"; + +import type { Context } from "./context"; +import type { ExtensionContext } from "vscode"; + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Project type. + */ +type Project = "MkDocs" | "Zensical"; + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Activate project-aware Markdown tagging. + * + * If a workspace folder contains an `mkdocs.yml` or `zensical.toml` file, open + * Markdown files in that folder are treated as Python Markdown for the current + * session. This makes it easier to work with Python Markdown in a project + * context without requiring explicit configuration in the workspace settings. + * + * @param context - Extension context + */ +export async function activateProjectMarkdown( + extension: ExtensionContext, + context: Context, +): Promise { + let projects = await findProjects(); + await tagOpenDocuments(projects); + + // Tag newly opened Markdown files in detected project folders + extension.subscriptions.push( + vscode.workspace.onDidOpenTextDocument((document) => { + void tagDocument(document, projects); + }), + ); + + // Re-scan project folders when the workspace shape changes + extension.subscriptions.push( + vscode.workspace.onDidChangeWorkspaceFolders(async () => { + projects = await findProjects(); + await tagOpenDocuments(projects); + }), + ); + + // Helper to refresh the list of detected project folders + const refresh = async (): Promise => { + projects = await findProjects(); + await tagOpenDocuments(projects); + }; + + // Create a file system watcher to detect changes to project config files + const watcher = vscode.workspace.createFileSystemWatcher( + "**/{mkdocs.yml,zensical.toml}", + ); + watcher.onDidCreate(() => void refresh()); + watcher.onDidDelete(() => void refresh()); + watcher.onDidChange(() => void refresh()); + extension.subscriptions.push(watcher); + + // Log detected project folders + context.log( + projects.size > 0 + ? `Detected ${projects.size} Python Markdown project folder(s)` + : "No Python Markdown project folders detected", + ); +} + +/* ---------------------------------------------------------------------------- + * Helper functions + * ------------------------------------------------------------------------- */ + +/** + * Find project folders in the current workspace. + * + * @returns Project folders + */ +async function findProjects(): Promise> { + const projects = new Map(); + for (const folder of vscode.workspace.workspaceFolders ?? []) { + const [match] = await vscode.workspace.findFiles( + new vscode.RelativePattern(folder, "**/{mkdocs.yml,zensical.toml}"), + null, + 1, + ); + + // Skip folders without a project config file + switch (basename(match?.fsPath ?? "")) { + case "zensical.toml": + projects.set(folder.uri.toString(), "Zensical"); + break; + case "mkdocs.yml": + projects.set(folder.uri.toString(), "MkDocs"); + break; + } + } + + // Return detected project folders + return projects; +} + +/** + * Tag all currently open Markdown documents inside detected project folders. + * + * @param projects - Project folders + */ +async function tagOpenDocuments(projects: Map): Promise { + for (const document of vscode.workspace.textDocuments) { + await tagDocument(document, projects); + } +} + +/** + * Tag a Markdown document as Python Markdown when appropriate. + * + * @param document - Text document + * @param projects - Project folders + */ +async function tagDocument( + document: vscode.TextDocument, + projects: Map, +): Promise { + if (document.languageId !== "markdown") { + return; + } + + // Skip non-file documents and non-Markdown files + if ( + document.uri.scheme !== "file" || + extname(document.uri.fsPath) !== ".md" + ) { + return; + } + + // Skip documents outside of detected project folders + const folder = vscode.workspace.getWorkspaceFolder(document.uri); + if (typeof folder === "undefined") { + return; + } + + // Skip documents in non-project folders + if (!projects.has(folder.uri.toString())) { + return; + } + + // Retag the document as Python Markdown + await vscode.languages.setTextDocumentLanguage(document, "python-markdown"); +} diff --git a/integrations/code/syntaxes/python-markdown.tmLanguage.json b/integrations/code/syntaxes/python-markdown.tmLanguage.json index 4ae3691..21e50e8 100644 --- a/integrations/code/syntaxes/python-markdown.tmLanguage.json +++ b/integrations/code/syntaxes/python-markdown.tmLanguage.json @@ -5,6 +5,9 @@ { "include": "#front_matter" }, + { + "include": "#snippet" + }, { "include": "#flow" } @@ -81,11 +84,319 @@ { "include": "#block_quote" }, + { + "include": "#list_item" + }, { "include": "#paragraph" } ] }, + "snippet": { + "patterns": [ + { + "name": "meta.snippet.section.markdown", + "match": "(?:^|\\G)(.*?)(?|(?|<--(?!-)" + }, + { + "name": "constant.other.smart-symbol.markdown", + "match": "(?