summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 11:33:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 11:33:32 +0000
commit1f403ad2197fc7442409f434ee574f3e6b46fb73 (patch)
tree0299c6dd11d5edfa918a29b6456bc1875f1d288c /tests
parentInitial commit. (diff)
downloadpygments-upstream.tar.xz
pygments-upstream.zip
Adding upstream version 2.14.0+dfsg.upstream/2.14.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py7
-rw-r--r--tests/conftest.py127
-rw-r--r--tests/contrast/min_contrasts.json50
-rw-r--r--tests/contrast/test_contrasts.py101
-rw-r--r--tests/dtds/HTML4-f.dtd37
-rw-r--r--tests/dtds/HTML4-s.dtd869
-rw-r--r--tests/dtds/HTML4.dcl88
-rw-r--r--tests/dtds/HTML4.dtd1092
-rw-r--r--tests/dtds/HTML4.soc9
-rw-r--r--tests/dtds/HTMLlat1.ent195
-rw-r--r--tests/dtds/HTMLspec.ent77
-rw-r--r--tests/dtds/HTMLsym.ent241
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_filename.html4
-rw-r--r--tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_nofilename.html4
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_nofilename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_filename.html6
-rw-r--r--tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_nofilename.html6
-rw-r--r--tests/snippets/apacheconf/test_directive_no_args.txt12
-rw-r--r--tests/snippets/apacheconf/test_fix_lock_absolute_path.txt8
-rw-r--r--tests/snippets/apacheconf/test_include_globs.txt8
-rw-r--r--tests/snippets/apacheconf/test_malformed_scoped_directive_closing_tag.txt19
-rw-r--r--tests/snippets/apacheconf/test_multi_include_globs.txt8
-rw-r--r--tests/snippets/apacheconf/test_multi_include_globs_root.txt8
-rw-r--r--tests/snippets/apacheconf/test_multiline_argument.txt20
-rw-r--r--tests/snippets/apacheconf/test_multiline_comment.txt12
-rw-r--r--tests/snippets/apacheconf/test_normal_scoped_directive.txt14
-rw-r--r--tests/snippets/apl/test_leading_underscore.txt26
-rw-r--r--tests/snippets/asm/test_cpuid.txt9
-rw-r--r--tests/snippets/bibtex/test_basic_bst.txt54
-rw-r--r--tests/snippets/bibtex/test_comment.txt7
-rw-r--r--tests/snippets/bibtex/test_entry.txt63
-rw-r--r--tests/snippets/bibtex/test_mismatched_brace.txt10
-rw-r--r--tests/snippets/bibtex/test_missing_body.txt10
-rw-r--r--tests/snippets/bibtex/test_preamble.txt11
-rw-r--r--tests/snippets/bibtex/test_string.txt15
-rw-r--r--tests/snippets/c/test_comment_end.txt31
-rw-r--r--tests/snippets/c/test_function_comments.txt409
-rw-r--r--tests/snippets/c/test_label.txt31
-rw-r--r--tests/snippets/c/test_label_followed_by_statement.txt35
-rw-r--r--tests/snippets/c/test_label_space_before_colon.txt32
-rw-r--r--tests/snippets/c/test_numbers.txt20
-rw-r--r--tests/snippets/c/test_preproc_file.txt17
-rw-r--r--tests/snippets/c/test_preproc_file2.txt17
-rw-r--r--tests/snippets/c/test_preproc_file3.txt18
-rw-r--r--tests/snippets/c/test_preproc_file4.txt13
-rw-r--r--tests/snippets/c/test_preproc_file5.txt19
-rw-r--r--tests/snippets/c/test_string_resembling_decl_end.txt41
-rw-r--r--tests/snippets/c/test_switch.txt56
-rw-r--r--tests/snippets/c/test_switch_space_before_colon.txt58
-rw-r--r--tests/snippets/cfm/test_basic_comment.txt8
-rw-r--r--tests/snippets/cfm/test_nested_comment.txt12
-rw-r--r--tests/snippets/coffeescript/test_beware_infinite_loop.txt14
-rw-r--r--tests/snippets/coffeescript/test_mixed_slashes.txt13
-rw-r--r--tests/snippets/conftest.py32
-rw-r--r--tests/snippets/console/fake_ps2_prompt.txt14
-rw-r--r--tests/snippets/console/prompt_in_output.txt21
-rw-r--r--tests/snippets/console/ps2_prompt.txt15
-rw-r--r--tests/snippets/console/test_comment_after_prompt.txt6
-rw-r--r--tests/snippets/console/test_newline_in_echo_no_ps2.txt16
-rw-r--r--tests/snippets/console/test_newline_in_echo_ps2.txt16
-rw-r--r--tests/snippets/console/test_newline_in_ls_no_ps2.txt16
-rw-r--r--tests/snippets/console/test_newline_in_ls_ps2.txt16
-rw-r--r--tests/snippets/console/test_virtualenv.txt11
-rw-r--r--tests/snippets/coq/test_unicode.txt15
-rw-r--r--tests/snippets/cpp/test_good_comment.txt6
-rw-r--r--tests/snippets/cpp/test_open_comment.txt5
-rw-r--r--tests/snippets/cpp/test_unicode_identifiers.txt146
-rw-r--r--tests/snippets/crystal/test_annotation.txt16
-rw-r--r--tests/snippets/crystal/test_array_access.txt11
-rw-r--r--tests/snippets/crystal/test_chars.txt25
-rw-r--r--tests/snippets/crystal/test_constant_and_module.txt14
-rw-r--r--tests/snippets/crystal/test_escaped_bracestring.txt19
-rw-r--r--tests/snippets/crystal/test_escaped_interpolation.txt9
-rw-r--r--tests/snippets/crystal/test_interpolation_nested_curly.txt56
-rw-r--r--tests/snippets/crystal/test_lib.txt58
-rw-r--r--tests/snippets/crystal/test_macro.txt76
-rw-r--r--tests/snippets/crystal/test_operator_methods.txt18
-rw-r--r--tests/snippets/crystal/test_percent_strings.txt41
-rw-r--r--tests/snippets/crystal/test_percent_strings_special.txt31
-rw-r--r--tests/snippets/crystal/test_pseudo_builtins.txt20
-rw-r--r--tests/snippets/crystal/test_pseudo_keywords.txt50
-rw-r--r--tests/snippets/crystal/test_range_syntax1.txt8
-rw-r--r--tests/snippets/crystal/test_range_syntax2.txt10
-rw-r--r--tests/snippets/csound/test_braced_strings.txt11
-rw-r--r--tests/snippets/csound/test_comments.txt16
-rw-r--r--tests/snippets/csound/test_escape_sequences.txt122
-rw-r--r--tests/snippets/csound/test_function_like_macro_definitions.txt44
-rw-r--r--tests/snippets/csound/test_function_like_macros.txt40
-rw-r--r--tests/snippets/csound/test_global_value_identifiers.txt30
-rw-r--r--tests/snippets/csound/test_goto_statements.txt176
-rw-r--r--tests/snippets/csound/test_include_directives.txt14
-rw-r--r--tests/snippets/csound/test_includestr_directives.txt11
-rw-r--r--tests/snippets/csound/test_instrument_blocks.txt42
-rw-r--r--tests/snippets/csound/test_keywords.txt62
-rw-r--r--tests/snippets/csound/test_labels.txt13
-rw-r--r--tests/snippets/csound/test_macro_preprocessor_directives.txt20
-rw-r--r--tests/snippets/csound/test_name.txt9
-rw-r--r--tests/snippets/csound/test_numbers.txt52
-rw-r--r--tests/snippets/csound/test_object_like_macro_definitions.txt30
-rw-r--r--tests/snippets/csound/test_operators.txt114
-rw-r--r--tests/snippets/csound/test_other_preprocessor_directives.txt26
-rw-r--r--tests/snippets/csound/test_printks_and_prints_escape_sequences.txt290
-rw-r--r--tests/snippets/csound/test_quoted_strings.txt9
-rw-r--r--tests/snippets/csound/test_user_defined_opcodes.txt24
-rw-r--r--tests/snippets/doscon/test_gt_only.txt11
-rw-r--r--tests/snippets/elpi/test_catastrophic_backtracking.txt6
-rw-r--r--tests/snippets/elpi/test_chr.txt54
-rw-r--r--tests/snippets/elpi/test_clause.txt67
-rw-r--r--tests/snippets/elpi/test_namespace.txt35
-rw-r--r--tests/snippets/elpi/test_pred.txt60
-rw-r--r--tests/snippets/elpi/test_type.txt112
-rw-r--r--tests/snippets/ezhil/test_function.txt100
-rw-r--r--tests/snippets/ezhil/test_gcd_expr.txt21
-rw-r--r--tests/snippets/ezhil/test_if_statement.txt28
-rw-r--r--tests/snippets/ezhil/test_sum.txt8
-rw-r--r--tests/snippets/fortran/test_string_cataback.txt112
-rw-r--r--tests/snippets/gas/test_comments.txt29
-rw-r--r--tests/snippets/gdscript/test_comment.txt6
-rw-r--r--tests/snippets/gdscript/test_export_array.txt17
-rw-r--r--tests/snippets/gdscript/test_function_with_types.txt33
-rw-r--r--tests/snippets/gdscript/test_inner_class.txt20
-rw-r--r--tests/snippets/gdscript/test_multiline_string.txt8
-rw-r--r--tests/snippets/gdscript/test_signal.txt15
-rw-r--r--tests/snippets/gdscript/test_simple_function.txt22
-rw-r--r--tests/snippets/gdscript/test_variable_declaration_and_assigment.txt12
-rw-r--r--tests/snippets/haskell/test_promoted_names.txt10
-rw-r--r--tests/snippets/html/multiline-comment-catastrophic-backtracking.txt34
-rw-r--r--tests/snippets/http/test_application_calendar_xml.txt28
-rw-r--r--tests/snippets/http/test_application_xml.txt28
-rw-r--r--tests/snippets/http/test_http_status_line.txt12
-rw-r--r--tests/snippets/http/test_http_status_line_without_reason_phrase.txt10
-rw-r--r--tests/snippets/http/test_http_status_line_without_reason_phrase_rfc_7230.txt11
-rw-r--r--tests/snippets/idris/test_compiler_directive.txt20
-rw-r--r--tests/snippets/idris/test_reserved_word.txt29
-rw-r--r--tests/snippets/ini/test_indented_entries_1.txt16
-rw-r--r--tests/snippets/ini/test_indented_entries_2.txt20
-rw-r--r--tests/snippets/ini/test_indented_entries_3.txt20
-rw-r--r--tests/snippets/j/test_deal_operator.txt8
-rw-r--r--tests/snippets/j/test_deal_operator_fixed_seed.txt9
-rw-r--r--tests/snippets/java/test_default.txt36
-rw-r--r--tests/snippets/java/test_enhanced_for.txt22
-rw-r--r--tests/snippets/java/test_multiline_string.txt185
-rw-r--r--tests/snippets/java/test_multiline_string_only.txt46
-rw-r--r--tests/snippets/java/test_numeric_literals.txt34
-rw-r--r--tests/snippets/java/test_record.txt67
-rw-r--r--tests/snippets/js/super.txt72
-rw-r--r--tests/snippets/jslt/test_sample.txt83
-rw-r--r--tests/snippets/json/test_basic.txt30
-rw-r--r--tests/snippets/json/test_basic_bare.txt23
-rw-r--r--tests/snippets/julia-repl/test_repl.txt51
-rw-r--r--tests/snippets/julia/test_keywords.txt101
-rw-r--r--tests/snippets/julia/test_macros.txt56
-rw-r--r--tests/snippets/julia/test_names.txt148
-rw-r--r--tests/snippets/julia/test_numbers.txt261
-rw-r--r--tests/snippets/julia/test_operators.txt172
-rw-r--r--tests/snippets/julia/test_strings.txt225
-rw-r--r--tests/snippets/julia/test_symbols.txt78
-rw-r--r--tests/snippets/julia/test_types.txt196
-rw-r--r--tests/snippets/julia/test_unicode.txt37
-rw-r--r--tests/snippets/kotlin/test_can_cope_generics_in_destructuring.txt27
-rw-r--r--tests/snippets/kotlin/test_can_cope_with_backtick_names_in_functions.txt8
-rw-r--r--tests/snippets/kotlin/test_can_cope_with_commas_and_dashes_in_backtick_Names.txt8
-rw-r--r--tests/snippets/kotlin/test_can_cope_with_destructuring.txt16
-rw-r--r--tests/snippets/kotlin/test_can_cope_with_generics.txt34
-rw-r--r--tests/snippets/kotlin/test_modifier_keyword.txt18
-rw-r--r--tests/snippets/kotlin/test_should_cope_with_multiline_comments.txt12
-rw-r--r--tests/snippets/kotlin/test_string_interpolation.txt35
-rw-r--r--tests/snippets/less/test_single_line_comments.txt21
-rw-r--r--tests/snippets/mason/test_handles_tags_correctly.txt69
-rw-r--r--tests/snippets/matlab/test_classes_with_properties.txt105
-rw-r--r--tests/snippets/matlab/test_command_mode.txt12
-rw-r--r--tests/snippets/matlab/test_comment_after_continuation.txt25
-rw-r--r--tests/snippets/matlab/test_dot_operator.txt10
-rw-r--r--tests/snippets/matlab/test_keywords_ended_by_newline.txt36
-rw-r--r--tests/snippets/matlab/test_line_continuation.txt25
-rw-r--r--tests/snippets/matlab/test_multiple_spaces_variable_assignment.txt13
-rw-r--r--tests/snippets/matlab/test_one_space_assignment.txt13
-rw-r--r--tests/snippets/matlab/test_operator_multiple_space.txt13
-rw-r--r--tests/snippets/matlab/test_single_line.txt18
-rw-r--r--tests/snippets/matlabsession/test_wrong_continuation.txt18
-rw-r--r--tests/snippets/mcfunction/commenting.txt173
-rw-r--r--tests/snippets/mcfunction/coordinates.txt188
-rw-r--r--tests/snippets/mcfunction/data.txt120
-rw-r--r--tests/snippets/mcfunction/difficult_1.txt56
-rw-r--r--tests/snippets/mcfunction/multiline.txt108
-rw-r--r--tests/snippets/mcfunction/selectors.txt73
-rw-r--r--tests/snippets/mcfunction/simple.txt92
-rw-r--r--tests/snippets/md/test_bold_fenced_by_asterisk.txt15
-rw-r--r--tests/snippets/md/test_bold_fenced_by_underscore.txt15
-rw-r--r--tests/snippets/md/test_bulleted_list_1.txt14
-rw-r--r--tests/snippets/md/test_bulleted_list_2.txt14
-rw-r--r--tests/snippets/md/test_bulleted_list_3.txt14
-rw-r--r--tests/snippets/md/test_bulleted_list_4.txt19
-rw-r--r--tests/snippets/md/test_code_block_fenced_by_backticks.txt15
-rw-r--r--tests/snippets/md/test_code_block_with_language.txt16
-rw-r--r--tests/snippets/md/test_escape_italics.txt23
-rw-r--r--tests/snippets/md/test_inline_code.txt36
-rw-r--r--tests/snippets/md/test_inline_code_after_block.txt19
-rw-r--r--tests/snippets/md/test_inline_code_in_list.txt26
-rw-r--r--tests/snippets/md/test_invalid_bold.txt31
-rw-r--r--tests/snippets/md/test_invalid_italics.txt31
-rw-r--r--tests/snippets/md/test_italics_and_bold.txt21
-rw-r--r--tests/snippets/md/test_italics_fenced_by_asterisk.txt15
-rw-r--r--tests/snippets/md/test_italics_fenced_by_underscore.txt15
-rw-r--r--tests/snippets/md/test_italics_no_multiline.txt10
-rw-r--r--tests/snippets/md/test_links.txt23
-rw-r--r--tests/snippets/md/test_mentions.txt10
-rw-r--r--tests/snippets/md/test_numbered_list.txt14
-rw-r--r--tests/snippets/md/test_quote.txt10
-rw-r--r--tests/snippets/md/test_reference_style_links.txt18
-rw-r--r--tests/snippets/md/test_strikethrough.txt9
-rw-r--r--tests/snippets/md/test_task_list.txt34
-rw-r--r--tests/snippets/md/test_topics.txt10
-rw-r--r--tests/snippets/mips/deprecated_substrings.txt34
-rw-r--r--tests/snippets/mips/keyword_substrings.txt254
-rw-r--r--tests/snippets/mips/variable_substrings.txt102
-rw-r--r--tests/snippets/nasm/checkid.txt32
-rw-r--r--tests/snippets/objectivec/test_literal_number_bool.txt7
-rw-r--r--tests/snippets/objectivec/test_literal_number_bool_expression.txt9
-rw-r--r--tests/snippets/objectivec/test_literal_number_expression.txt11
-rw-r--r--tests/snippets/objectivec/test_literal_number_int.txt9
-rw-r--r--tests/snippets/objectivec/test_literal_number_nested_expression.txt15
-rw-r--r--tests/snippets/objectivec/test_module_import.txt9
-rw-r--r--tests/snippets/octave/test_multilinecomment.txt27
-rw-r--r--tests/snippets/omg-idl/annotation_named_params.txt27
-rw-r--r--tests/snippets/omg-idl/enumerators.txt18
-rw-r--r--tests/snippets/peg/test_basic.txt17
-rw-r--r--tests/snippets/peg/test_modified_strings.txt21
-rw-r--r--tests/snippets/peg/test_operators.txt29
-rw-r--r--tests/snippets/php/test_backslashes_in_strings.txt28
-rw-r--r--tests/snippets/php/test_string_escaping_run.txt16
-rw-r--r--tests/snippets/powershell/test_colon_punctuation.txt35
-rw-r--r--tests/snippets/powershell/test_remoting_session.txt19
-rw-r--r--tests/snippets/powershell/test_session.txt28
-rw-r--r--tests/snippets/praat/test_broken_unquoted_string.txt16
-rw-r--r--tests/snippets/praat/test_function_call.txt20
-rw-r--r--tests/snippets/praat/test_inline_if.txt27
-rw-r--r--tests/snippets/praat/test_interpolated_indexed_numeric_with_precision.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_local_numeric_with_precision.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_numeric_hash.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_numeric_indexed.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_numeric_with_precision.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_string_hash.txt6
-rw-r--r--tests/snippets/praat/test_interpolated_string_indexed.txt6
-rw-r--r--tests/snippets/praat/test_interpolation_boundary.txt14
-rw-r--r--tests/snippets/praat/test_numeric_assignment.txt11
-rw-r--r--tests/snippets/praat/test_string_assignment.txt12
-rw-r--r--tests/snippets/praat/test_string_escaped_quotes.txt13
-rw-r--r--tests/snippets/promql/test_complex_exp_single_quotes.txt35
-rw-r--r--tests/snippets/promql/test_expression_and_comment.txt15
-rw-r--r--tests/snippets/promql/test_function_delta.txt19
-rw-r--r--tests/snippets/promql/test_function_multi_line.txt80
-rw-r--r--tests/snippets/promql/test_function_multi_line_with_offset.txt87
-rw-r--r--tests/snippets/promql/test_function_sum_with_args.txt19
-rw-r--r--tests/snippets/promql/test_matching_operator_no_regex_match.txt16
-rw-r--r--tests/snippets/promql/test_metric.txt6
-rw-r--r--tests/snippets/promql/test_metric_multiple_labels.txt19
-rw-r--r--tests/snippets/promql/test_metric_multiple_labels_with_spaces.txt22
-rw-r--r--tests/snippets/promql/test_metric_one_label.txt13
-rw-r--r--tests/snippets/properties/test_comments.txt12
-rw-r--r--tests/snippets/properties/test_escaped_space_in_key.txt10
-rw-r--r--tests/snippets/properties/test_escaped_space_in_value.txt10
-rw-r--r--tests/snippets/properties/test_just_key.txt6
-rw-r--r--tests/snippets/properties/test_just_key_with_space.txt6
-rw-r--r--tests/snippets/properties/test_leading_whitespace_comments.txt6
-rw-r--r--tests/snippets/properties/test_space_delimited_kv_pair.txt8
-rw-r--r--tests/snippets/pwsh-session/test_continuation.txt124
-rw-r--r--tests/snippets/python/test_bytes_escape_codes.txt24
-rw-r--r--tests/snippets/python/test_floats.txt75
-rw-r--r--tests/snippets/python/test_fstring_01a.txt25
-rw-r--r--tests/snippets/python/test_fstring_01b.txt25
-rw-r--r--tests/snippets/python/test_fstring_02a.txt13
-rw-r--r--tests/snippets/python/test_fstring_02b.txt13
-rw-r--r--tests/snippets/python/test_fstring_03a.txt14
-rw-r--r--tests/snippets/python/test_fstring_03b.txt14
-rw-r--r--tests/snippets/python/test_fstring_04a.txt13
-rw-r--r--tests/snippets/python/test_fstring_04b.txt13
-rw-r--r--tests/snippets/python/test_fstring_05a.txt16
-rw-r--r--tests/snippets/python/test_fstring_05b.txt16
-rw-r--r--tests/snippets/python/test_fstring_06a.txt16
-rw-r--r--tests/snippets/python/test_fstring_06b.txt16
-rw-r--r--tests/snippets/python/test_fstring_07a.txt17
-rw-r--r--tests/snippets/python/test_fstring_07b.txt17
-rw-r--r--tests/snippets/python/test_fstring_08a.txt15
-rw-r--r--tests/snippets/python/test_fstring_08b.txt15
-rw-r--r--tests/snippets/python/test_fstring_09a.txt14
-rw-r--r--tests/snippets/python/test_fstring_09b.txt14
-rw-r--r--tests/snippets/python/test_fstring_10a.txt18
-rw-r--r--tests/snippets/python/test_fstring_10b.txt18
-rw-r--r--tests/snippets/python/test_fstring_11a.txt18
-rw-r--r--tests/snippets/python/test_fstring_11b.txt18
-rw-r--r--tests/snippets/python/test_fstring_12a.txt16
-rw-r--r--tests/snippets/python/test_fstring_12b.txt16
-rw-r--r--tests/snippets/python/test_fstring_13a.txt17
-rw-r--r--tests/snippets/python/test_fstring_13b.txt17
-rw-r--r--tests/snippets/python/test_fstring_14a.txt20
-rw-r--r--tests/snippets/python/test_fstring_14b.txt20
-rw-r--r--tests/snippets/python/test_fstring_15a.txt42
-rw-r--r--tests/snippets/python/test_fstring_15b.txt42
-rw-r--r--tests/snippets/python/test_fstring_16a.txt18
-rw-r--r--tests/snippets/python/test_fstring_16b.txt18
-rw-r--r--tests/snippets/python/test_fstring_17a.txt14
-rw-r--r--tests/snippets/python/test_fstring_17b.txt14
-rw-r--r--tests/snippets/python/test_fstring_18a.txt25
-rw-r--r--tests/snippets/python/test_fstring_18b.txt25
-rw-r--r--tests/snippets/python/test_fstring_19a.txt46
-rw-r--r--tests/snippets/python/test_fstring_19b.txt46
-rw-r--r--tests/snippets/python/test_fstring_20a.txt17
-rw-r--r--tests/snippets/python/test_fstring_20b.txt17
-rw-r--r--tests/snippets/python/test_fstring_21a.txt15
-rw-r--r--tests/snippets/python/test_fstring_21b.txt15
-rw-r--r--tests/snippets/python/test_fstring_22a.txt14
-rw-r--r--tests/snippets/python/test_fstring_22b.txt14
-rw-r--r--tests/snippets/python/test_fstring_23a.txt11
-rw-r--r--tests/snippets/python/test_fstring_23b.txt11
-rw-r--r--tests/snippets/python/test_fstring_24a.txt23
-rw-r--r--tests/snippets/python/test_fstring_24b.txt23
-rw-r--r--tests/snippets/python/test_fstring_25a.txt24
-rw-r--r--tests/snippets/python/test_fstring_25b.txt24
-rw-r--r--tests/snippets/python/test_fstring_26a.txt20
-rw-r--r--tests/snippets/python/test_fstring_26b.txt20
-rw-r--r--tests/snippets/python/test_fstring_27a.txt11
-rw-r--r--tests/snippets/python/test_fstring_27b.txt11
-rw-r--r--tests/snippets/python/test_fstring_28a.txt11
-rw-r--r--tests/snippets/python/test_fstring_28b.txt11
-rw-r--r--tests/snippets/python/test_fstring_29a.txt15
-rw-r--r--tests/snippets/python/test_fstring_29b.txt15
-rw-r--r--tests/snippets/python/test_fstring_30a.txt16
-rw-r--r--tests/snippets/python/test_fstring_30b.txt16
-rw-r--r--tests/snippets/python/test_fstring_31a.txt15
-rw-r--r--tests/snippets/python/test_fstring_31b.txt15
-rw-r--r--tests/snippets/python/test_fstring_32a.txt15
-rw-r--r--tests/snippets/python/test_fstring_32b.txt15
-rw-r--r--tests/snippets/python/test_fstring_33a.txt15
-rw-r--r--tests/snippets/python/test_fstring_33b.txt15
-rw-r--r--tests/snippets/python/test_fstring_34a.txt20
-rw-r--r--tests/snippets/python/test_fstring_34b.txt20
-rw-r--r--tests/snippets/python/test_fstring_35a.txt15
-rw-r--r--tests/snippets/python/test_fstring_35b.txt15
-rw-r--r--tests/snippets/python/test_fstring_36a.txt16
-rw-r--r--tests/snippets/python/test_fstring_36b.txt16
-rw-r--r--tests/snippets/python/test_needs_name.txt55
-rw-r--r--tests/snippets/python/test_pep_515.txt28
-rw-r--r--tests/snippets/python/test_raw_fstring.txt46
-rw-r--r--tests/snippets/python/test_string_escape_codes.txt20
-rw-r--r--tests/snippets/python/test_walrus_operator.txt21
-rw-r--r--tests/snippets/python2/test_cls_builtin.txt34
-rw-r--r--tests/snippets/qbasic/test_keywords_with_dollar.txt22
-rw-r--r--tests/snippets/r/test_call.txt12
-rw-r--r--tests/snippets/r/test_custom_operator.txt10
-rw-r--r--tests/snippets/r/test_dot_indexing.txt9
-rw-r--r--tests/snippets/r/test_dot_name.txt10
-rw-r--r--tests/snippets/r/test_indexing.txt9
-rw-r--r--tests/snippets/r/test_name1.txt6
-rw-r--r--tests/snippets/r/test_name2.txt8
-rw-r--r--tests/snippets/r/test_name3.txt8
-rw-r--r--tests/snippets/ruby/test_escaped_bracestring.txt19
-rw-r--r--tests/snippets/ruby/test_interpolation_nested_curly.txt56
-rw-r--r--tests/snippets/ruby/test_operator_methods.txt9
-rw-r--r--tests/snippets/ruby/test_range_syntax1.txt8
-rw-r--r--tests/snippets/ruby/test_range_syntax2.txt8
-rw-r--r--tests/snippets/ruby/test_range_syntax3.txt10
-rw-r--r--tests/snippets/rust/test_attribute.txt12
-rw-r--r--tests/snippets/rust/test_break.txt39
-rw-r--r--tests/snippets/rust/test_rawstrings.txt117
-rw-r--r--tests/snippets/scala/test_colon_colon_function_name.txt33
-rw-r--r--tests/snippets/scala/test_default_parameter.txt37
-rw-r--r--tests/snippets/scala/test_end_val.txt8
-rw-r--r--tests/snippets/scala/test_end_valx.txt8
-rw-r--r--tests/snippets/scala/test_float_with_exponents.txt12
-rw-r--r--tests/snippets/scala/test_function_operator_name.txt18
-rw-r--r--tests/snippets/scala/test_import_path.txt12
-rw-r--r--tests/snippets/scala/test_invalid_symbol_and_invalid_char.txt8
-rw-r--r--tests/snippets/scala/test_open_soft_keyword.txt12
-rw-r--r--tests/snippets/scala/test_package_name.txt11
-rw-r--r--tests/snippets/scala/test_prepend_operator.txt10
-rw-r--r--tests/snippets/scala/test_qualified_name.txt10
-rw-r--r--tests/snippets/scala/test_qualified_name_class.txt10
-rw-r--r--tests/snippets/scala/test_script_header.txt6
-rw-r--r--tests/snippets/scala/test_symbol_followed_by_op.txt7
-rw-r--r--tests/snippets/scala/test_symbol_name_ending_with_star.txt6
-rw-r--r--tests/snippets/scala/test_underscore_name.txt12
-rw-r--r--tests/snippets/scheme/keywords.txt43
-rw-r--r--tests/snippets/scheme/numbers.txt169
-rw-r--r--tests/snippets/scheme/strings.txt85
-rw-r--r--tests/snippets/shell/test_array_nums.txt14
-rw-r--r--tests/snippets/shell/test_curly_no_escape_and_quotes.txt15
-rw-r--r--tests/snippets/shell/test_curly_with_escape.txt13
-rw-r--r--tests/snippets/shell/test_end_of_line_nums.txt15
-rw-r--r--tests/snippets/shell/test_parsed_single.txt8
-rw-r--r--tests/snippets/shell/test_short_variable_names.txt26
-rw-r--r--tests/snippets/shexc/test_prefixed_name_starting_with_number.txt8
-rw-r--r--tests/snippets/smarty/test_nested_curly.txt18
-rw-r--r--tests/snippets/snbt/json.txt43
-rw-r--r--tests/snippets/snbt/literals.txt41
-rw-r--r--tests/snippets/snbt/multiline.txt56
-rw-r--r--tests/snippets/snbt/nesting.txt39
-rw-r--r--tests/snippets/snbt/quoted_keys.txt29
-rw-r--r--tests/snippets/systemverilog/test_basic.txt157
-rw-r--r--tests/snippets/systemverilog/test_classes.txt89
-rw-r--r--tests/snippets/systemverilog/test_numbers.txt158
-rw-r--r--tests/snippets/systemverilog/test_operators.txt213
-rw-r--r--tests/snippets/tcl/test_comma_and_at.txt131
-rw-r--r--tests/snippets/tcl/test_vars.txt17
-rw-r--r--tests/snippets/teal/test_comments.txt28
-rw-r--r--tests/snippets/teal/test_literals.txt28
-rw-r--r--tests/snippets/teal/test_strings.txt15
-rw-r--r--tests/snippets/terraform/test_attributes.txt155
-rw-r--r--tests/snippets/terraform/test_backend.txt44
-rw-r--r--tests/snippets/terraform/test_comment.txt64
-rw-r--r--tests/snippets/terraform/test_functions.txt56
-rw-r--r--tests/snippets/terraform/test_heredoc.txt65
-rw-r--r--tests/snippets/terraform/test_module.txt32
-rw-r--r--tests/snippets/terraform/test_resource.txt211
-rw-r--r--tests/snippets/terraform/test_types.txt94
-rw-r--r--tests/snippets/terraform/test_variable_declaration.txt41
-rw-r--r--tests/snippets/terraform/test_variable_read.txt23
-rw-r--r--tests/snippets/turtle/test_prefixed_name_starting_with_number.txt8
-rw-r--r--tests/snippets/typescript/test_function_definition.txt18
-rw-r--r--tests/snippets/unixconfig/etc_group.txt45
-rw-r--r--tests/snippets/unixconfig/etc_passwd.txt86
-rw-r--r--tests/snippets/unixconfig/etc_shadow.txt74
-rw-r--r--tests/snippets/usd/test_attribute.txt174
-rw-r--r--tests/snippets/usd/test_composition_arcs.txt101
-rw-r--r--tests/snippets/usd/test_metadata.txt36
-rw-r--r--tests/snippets/usd/test_numbers.txt21
-rw-r--r--tests/snippets/usd/test_outer_match_at_sign.txt14
-rw-r--r--tests/snippets/usd/test_outer_match_double.txt12
-rw-r--r--tests/snippets/usd/test_outer_match_single.txt12
-rw-r--r--tests/snippets/usd/test_string_multiple_line.txt20
-rw-r--r--tests/snippets/usd/test_string_priority.txt10
-rw-r--r--tests/snippets/usd/test_string_single_line.txt6
-rw-r--r--tests/snippets/vbscript/test_floats.txt34
-rw-r--r--tests/snippets/vbscript/test_floats_multiple.txt7
-rw-r--r--tests/snippets/vbscript/test_integers.txt14
-rw-r--r--tests/snippets/vbscript/test_invalid_character.txt10
-rw-r--r--tests/snippets/vbscript/test_names.txt18
-rw-r--r--tests/snippets/vbscript/test_reject_almost_float.txt7
-rw-r--r--tests/snippets/vbscript/test_unterminated_string.txt7
-rw-r--r--tests/snippets/wat/test_align_and_offset_accept_hexadecimal_numbers.txt14
-rw-r--r--tests/snippets/wat/test_comment_with_open_paren.txt10
-rw-r--r--tests/snippets/wat/test_comment_with_semicolon.txt10
-rw-r--r--tests/snippets/wat/test_i32_const_is_builtin.txt6
-rw-r--r--tests/snippets/wat/test_multiline_comment.txt11
-rw-r--r--tests/snippets/wat/test_nested_comment.txt14
-rw-r--r--tests/snippets/wat/test_string_byte_escape.txt9
-rw-r--r--tests/snippets/wat/test_string_with_escape.txt9
-rw-r--r--tests/snippets/wat/test_variable_name_pattern.txt6
-rw-r--r--tests/snippets/whiley/test_whiley_operator.txt10
-rw-r--r--tests/snippets/wren/lonely-paren.txt10
-rw-r--r--tests/snippets/xml/multiline-comment-catastrophic-backtracking.txt56
-rw-r--r--tests/snippets/yaml/test_yaml.txt13
-rw-r--r--tests/snippets/yaml/test_yaml_colon_in_key.txt11
-rw-r--r--tests/snippets/yaml/test_yaml_colon_in_key_double.txt11
-rw-r--r--tests/snippets/yaml/test_yaml_colon_in_key_start.txt11
-rw-r--r--tests/snippets/yang/test_float_value.txt11
-rw-r--r--tests/snippets/yang/test_integer_value.txt11
-rw-r--r--tests/snippets/yang/test_namespace_1.txt11
-rw-r--r--tests/snippets/yang/test_namespace_2.txt13
-rw-r--r--tests/snippets/yang/test_revision_date.txt11
-rw-r--r--tests/snippets/yang/test_string_value.txt11
-rw-r--r--tests/support/empty.py0
-rw-r--r--tests/support/html_formatter.py5
-rw-r--r--tests/support/python_lexer.py11
-rw-r--r--tests/support/structural_diff.py37
-rw-r--r--tests/support/tags36
-rw-r--r--tests/test_basic_api.py351
-rw-r--r--tests/test_cmdline.py324
-rw-r--r--tests/test_coffeescript.py52
-rw-r--r--tests/test_crystal.py80
-rw-r--r--tests/test_data.py285
-rw-r--r--tests/test_devicetree_lexer.py32
-rw-r--r--tests/test_func.py44
-rw-r--r--tests/test_groff_formatter.py40
-rw-r--r--tests/test_guess.py184
-rw-r--r--tests/test_html_formatter.py271
-rw-r--r--tests/test_html_formatter_linenos_elements.py63
-rw-r--r--tests/test_html_lexer.py131
-rw-r--r--tests/test_inherit.py101
-rw-r--r--tests/test_irc_formatter.py30
-rw-r--r--tests/test_java.py40
-rw-r--r--tests/test_javascript.py84
-rw-r--r--tests/test_latex_formatter.py107
-rw-r--r--tests/test_markdown_lexer.py178
-rw-r--r--tests/test_modeline.py20
-rw-r--r--tests/test_mysql.py273
-rw-r--r--tests/test_pangomarkup_formatter.py44
-rw-r--r--tests/test_perllexer.py190
-rw-r--r--tests/test_procfile.py40
-rw-r--r--tests/test_raw_token.py68
-rw-r--r--tests/test_regexlexer.py65
-rw-r--r--tests/test_regexopt.py102
-rw-r--r--tests/test_robotframework_lexer.py38
-rw-r--r--tests/test_rtf_formatter.py107
-rw-r--r--tests/test_ruby.py54
-rw-r--r--tests/test_sql.py115
-rw-r--r--tests/test_templates.py130
-rw-r--r--tests/test_terminal_formatter.py100
-rw-r--r--tests/test_thingsdb.py36
-rw-r--r--tests/test_tnt.py226
-rw-r--r--tests/test_token.py51
-rw-r--r--tests/test_unistring.py45
-rwxr-xr-xtests/test_usd.py64
-rw-r--r--tests/test_using_api.py39
-rw-r--r--tests/test_util.py189
-rw-r--r--tests/test_words.py366
629 files changed, 23134 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..3248d17
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,7 @@
+"""
+ Pygments test package
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..e3cb2c0
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,127 @@
+"""
+ Generated lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Checks that lexers output the expected tokens for each sample
+ under snippets/ and examplefiles/.
+
+ After making a change, rather than updating the samples manually,
+ run `pytest --update-goldens <changed file>`.
+
+ To add a new sample, create a new file matching this pattern.
+ The directory must match the alias of the lexer to be used.
+ Populate only the input, then just `--update-goldens`.
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pathlib import Path
+
+import pytest
+
+import pygments.lexers
+from pygments.token import Error
+
+
+def pytest_addoption(parser):
+ parser.addoption('--update-goldens', action='store_true',
+ help='reset golden master benchmarks')
+
+
+class LexerTestItem(pytest.Item):
+ def __init__(self, name, parent):
+ super().__init__(name, parent)
+ self.lexer = Path(str(self.fspath)).parent.name
+ self.actual = None
+
+ @classmethod
+ def _prettyprint_tokens(cls, tokens):
+ for tok, val in tokens:
+ if tok is Error and not cls.allow_errors:
+ raise ValueError('generated Error token at {!r}'.format(val))
+ yield '{!r:<13} {}'.format(val, str(tok)[6:])
+ if val.endswith('\n'):
+ yield ''
+
+ def runtest(self):
+ lexer = pygments.lexers.get_lexer_by_name(self.lexer)
+ tokens = lexer.get_tokens(self.input)
+ self.actual = '\n'.join(self._prettyprint_tokens(tokens)).rstrip('\n') + '\n'
+ if not self.config.getoption('--update-goldens'):
+ assert self.actual == self.expected
+
+ def _test_file_rel_path(self):
+ return Path(str(self.fspath)).relative_to(Path(__file__).parent.parent)
+
+ def _prunetraceback(self, excinfo):
+ excinfo.traceback = excinfo.traceback.cut(__file__).filter()
+
+ def repr_failure(self, excinfo):
+ if isinstance(excinfo.value, AssertionError):
+ rel_path = self._test_file_rel_path()
+ message = (
+ 'The tokens produced by the "{}" lexer differ from the '
+ 'expected ones in the file "{}".\n'
+ 'Run `pytest {} --update-goldens` to update it.'
+ ).format(self.lexer, rel_path, Path(*rel_path.parts[:2]))
+ diff = str(excinfo.value).split('\n', 1)[-1]
+ return message + '\n\n' + diff
+ else:
+ return pytest.Item.repr_failure(self, excinfo)
+
+ def reportinfo(self):
+ return self.fspath, None, str(self._test_file_rel_path())
+
+ def maybe_overwrite(self):
+ if self.actual is not None and self.config.getoption('--update-goldens'):
+ self.overwrite()
+
+
+class LexerSeparateTestItem(LexerTestItem):
+ allow_errors = False
+
+ def __init__(self, name, parent):
+ super().__init__(name, parent)
+
+ self.input = self.fspath.read_text('utf-8')
+ output_path = self.fspath + '.output'
+ if output_path.check():
+ self.expected = output_path.read_text(encoding='utf-8')
+ else:
+ self.expected = ''
+
+ def overwrite(self):
+ output_path = self.fspath + '.output'
+ output_path.write_text(self.actual, encoding='utf-8')
+
+
+class LexerInlineTestItem(LexerTestItem):
+ allow_errors = True
+
+ def __init__(self, name, parent):
+ super().__init__(name, parent)
+
+ content = self.fspath.read_text('utf-8')
+ content, _, self.expected = content.partition('\n---tokens---\n')
+ if content.startswith('---input---\n'):
+ content = '\n' + content
+ self.comment, _, self.input = content.rpartition('\n---input---\n')
+ if not self.input.endswith('\n'):
+ self.input += '\n'
+ self.comment = self.comment.strip()
+
+ def overwrite(self):
+ with self.fspath.open('w', encoding='utf-8') as f:
+ f.write(self.comment)
+ if self.comment:
+ f.write('\n\n')
+ f.write('---input---\n')
+ f.write(self.input)
+ f.write('\n---tokens---\n')
+ f.write(self.actual)
+
+
+def pytest_runtest_teardown(item, nextitem):
+ if isinstance(item, LexerTestItem):
+ item.maybe_overwrite()
diff --git a/tests/contrast/min_contrasts.json b/tests/contrast/min_contrasts.json
new file mode 100644
index 0000000..a69d73f
--- /dev/null
+++ b/tests/contrast/min_contrasts.json
@@ -0,0 +1,50 @@
+{
+ "default": 4.5,
+ "emacs": 2.4,
+ "friendly": 2.2,
+ "friendly_grayscale": 2.2,
+ "colorful": 2.2,
+ "autumn": 2.2,
+ "murphy": 1.4,
+ "manni": 1.4,
+ "material": 1.6,
+ "monokai": 2.3,
+ "perldoc": 2.7,
+ "pastie": 2.5,
+ "borland": 2.3,
+ "trac": 2.3,
+ "native": 3.0,
+ "fruity": 1.6,
+ "bw": 21.0,
+ "vim": 1.3,
+ "vs": 3.6,
+ "tango": 2.4,
+ "rrt": 5.3,
+ "xcode": 5.1,
+ "igor": 3.6,
+ "paraiso-light": 1.3,
+ "paraiso-dark": 1.3,
+ "lovelace": 3.5,
+ "algol": 3.5,
+ "algol_nu": 3.5,
+ "arduino": 2.6,
+ "rainbow_dash": 2.1,
+ "abap": 2.5,
+ "solarized-dark": 1.5,
+ "solarized-light": 1.0,
+ "sas": 4.6,
+ "staroffice": 4.5,
+ "stata": 2.4,
+ "stata-light": 2.4,
+ "stata-dark": 3.4,
+ "inkpot": 1.0,
+ "zenburn": 1.6,
+ "gruvbox-dark": 4.0,
+ "gruvbox-light": 3.2,
+ "dracula": 1.4,
+ "one-dark": 3.7,
+ "lilypond": 2.3,
+ "nord": 2.4,
+ "nord-darker": 2.8,
+ "github-dark": 4.8
+} \ No newline at end of file
diff --git a/tests/contrast/test_contrasts.py b/tests/contrast/test_contrasts.py
new file mode 100644
index 0000000..517b34e
--- /dev/null
+++ b/tests/contrast/test_contrasts.py
@@ -0,0 +1,101 @@
+"""
+ Contrast Tests
+ ~~~~~~~~~~
+
+ Pygments styles should be accessible to people with suboptimal vision.
+ This test ensures that the minimum contrast of styles does not degrade,
+ and that every rule of a new style fulfills the WCAG AA standard.[1]
+
+ [1]: https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html
+"""
+
+import json
+import os
+import sys
+
+import pygments.styles
+import pygments.token
+import wcag_contrast_ratio
+
+JSON_FILENAME = os.path.join(os.path.dirname(__file__), "min_contrasts.json")
+WCAG_AA_CONTRAST = 4.5
+
+
+def hex2rgb(hexstr):
+ hexstr = hexstr.lstrip("#")
+ r = int(hexstr[:2], 16) / 255
+ g = int(hexstr[2:4], 16) / 255
+ b = int(hexstr[4:], 16) / 255
+ return (r, g, b)
+
+
+def get_style_contrasts(style_cls):
+ return [
+ (
+ round(
+ wcag_contrast_ratio.rgb(
+ hex2rgb(style["bgcolor"] or style_cls.background_color),
+ hex2rgb(style["color"] or "#000000")
+ # we default to black because browsers also do
+ ),
+ 1,
+ ),
+ ttype,
+ )
+ for ttype, style in style_cls.list_styles()
+ if ttype != pygments.token.Whitespace
+ ]
+
+
+def builtin_styles():
+ for style_name in pygments.styles.STYLE_MAP:
+ yield (style_name, pygments.styles.get_style_by_name(style_name))
+
+
+def min_contrasts():
+ return {
+ name: min(x[0] for x in get_style_contrasts(style))
+ for name, style in builtin_styles()
+ }
+
+
+def update_json():
+ with open(JSON_FILENAME, "w") as f:
+ json.dump(
+ min_contrasts(),
+ f,
+ indent=2,
+ )
+
+
+def test_contrasts(fail_if_improved=True):
+ with open(JSON_FILENAME) as f:
+ previous_contrasts = json.load(f)
+
+ for style_name in pygments.styles.STYLE_MAP:
+ style = pygments.styles.get_style_by_name(style_name)
+ contrasts = get_style_contrasts(style)
+ min_contrast = min([x[0] for x in contrasts])
+
+ bar = previous_contrasts.get(style_name, WCAG_AA_CONTRAST)
+
+ assert not min_contrast < bar, (
+ "contrast degradation for style '{}'\n"
+ "The following rules have a contrast lower than the required {}:\n\n"
+ "{}\n"
+ ).format(
+ style_name,
+ bar,
+ "\n".join(
+ [
+ "* {:.2f} {}".format(contrast, ttype)
+ for contrast, ttype in contrasts
+ if contrast < bar
+ ]
+ ),
+ )
+
+ if fail_if_improved:
+ assert (
+ not min_contrast > bar
+ ), "congrats, you improved a contrast! please run ./scripts/update_contrasts.py"
diff --git a/tests/dtds/HTML4-f.dtd b/tests/dtds/HTML4-f.dtd
new file mode 100644
index 0000000..9552012
--- /dev/null
+++ b/tests/dtds/HTML4-f.dtd
@@ -0,0 +1,37 @@
+<!--
+ This is the HTML 4.0 Frameset DTD, which should be
+ used for documents with frames. This DTD is identical
+ to the HTML 4.0 Transitional DTD except for the
+ content model of the "HTML" element: in frameset
+ documents, the "FRAMESET" element replaces the "BODY"
+ element.
+
+ Draft: $Date: 1999/05/02 15:37:15 $
+
+ Authors:
+ Dave Raggett <dsr@w3.org>
+ Arnaud Le Hors <lehors@w3.org>
+ Ian Jacobs <ij@w3.org>
+
+ Further information about HTML 4.0 is available at:
+
+ http://www.w3.org/TR/REC-html40.
+-->
+<!ENTITY % HTML.Version "-//W3C//DTD HTML 4.0 Frameset//EN"
+ -- Typical usage:
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"
+ "http://www.w3.org/TR/REC-html40/frameset.dtd">
+ <html>
+ <head>
+ ...
+ </head>
+ <frameset>
+ ...
+ </frameset>
+ </html>
+-->
+
+<!ENTITY % HTML.Frameset "INCLUDE">
+<!ENTITY % HTML4.dtd PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+%HTML4.dtd; \ No newline at end of file
diff --git a/tests/dtds/HTML4-s.dtd b/tests/dtds/HTML4-s.dtd
new file mode 100644
index 0000000..8ce7917
--- /dev/null
+++ b/tests/dtds/HTML4-s.dtd
@@ -0,0 +1,869 @@
+<!--
+ This is HTML 4.0 Strict DTD, which excludes the presentation
+ attributes and elements that W3C expects to phase out as
+ support for style sheets matures. Authors should use the Strict
+ DTD when possible, but may use the Transitional DTD when support
+ for presentation attribute and elements is required.
+
+ HTML 4.0 includes mechanisms for style sheets, scripting,
+ embedding objects, improved support for right to left and mixed
+ direction text, and enhancements to forms for improved
+ accessibility for people with disabilities.
+
+ Draft: $Date: 1999/05/02 15:37:15 $
+
+ Authors:
+ Dave Raggett <dsr@w3.org>
+ Arnaud Le Hors <lehors@w3.org>
+ Ian Jacobs <ij@w3.org>
+
+ Further information about HTML 4.0 is available at:
+
+ http://www.w3.org/TR/REC-html40
+-->
+<!--
+ Typical usage:
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
+ "http://www.w3.org/TR/REC-html40/strict.dtd">
+ <html>
+ <head>
+ ...
+ </head>
+ <body>
+ ...
+ </body>
+ </html>
+
+ The URI used as a system identifier with the public identifier allows
+ the user agent to download the DTD and entity sets as needed.
+
+ The FPI for the Transitional HTML 4.0 DTD is:
+
+ "-//W3C//DTD HTML 4.0 Transitional//EN
+
+ and its URI is:
+
+ http://www.w3.org/TR/REC-html40/loose.dtd
+
+ If you are writing a document that includes frames, use
+ the following FPI:
+
+ "-//W3C//DTD HTML 4.0 Frameset//EN"
+
+ with the URI:
+
+ http://www.w3.org/TR/REC-html40/frameset.dtd
+
+ The following URIs are supported in relation to HTML 4.0
+
+ "http://www.w3.org/TR/REC-html40/strict.dtd" (Strict DTD)
+ "http://www.w3.org/TR/REC-html40/loose.dtd" (Loose DTD)
+ "http://www.w3.org/TR/REC-html40/frameset.dtd" (Frameset DTD)
+ "http://www.w3.org/TR/REC-html40/HTMLlat1.ent" (Latin-1 entities)
+ "http://www.w3.org/TR/REC-html40/HTMLsymbol.ent" (Symbol entities)
+ "http://www.w3.org/TR/REC-html40/HTMLspecial.ent" (Special entities)
+
+ These URIs point to the latest version of each file. To reference
+ this specific revision use the following URIs:
+
+ "http://www.w3.org/TR/REC-html40-971218/strict.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/loose.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/frameset.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLlat1.ent"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLsymbol.ent"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLspecial.ent"
+
+-->
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA"
+ -- media type, as per [RFC2045]
+ -->
+
+<!ENTITY % ContentTypes "CDATA"
+ -- comma-separated list of media types, as per [RFC2045]
+ -->
+
+<!ENTITY % Charset "CDATA"
+ -- a character encoding, as per [RFC2045]
+ -->
+
+<!ENTITY % Charsets "CDATA"
+ -- a space separated list of character encodings, as per [RFC2045]
+ -->
+
+<!ENTITY % LanguageCode "NAME"
+ -- a language code, as per [RFC1766]
+ -->
+
+<!ENTITY % Character "CDATA"
+ -- a single character from [ISO10646]
+ -->
+
+<!ENTITY % LinkTypes "CDATA"
+ -- space-separated list of link types
+ -->
+
+<!ENTITY % MediaDesc "CDATA"
+ -- single or comma-separated list of media descriptors
+ -->
+
+<!ENTITY % URI "CDATA"
+ -- a Uniform Resource Identifier,
+ see [URI]
+ -->
+
+<!ENTITY % Datetime "CDATA" -- date and time information. ISO date format -->
+
+
+<!ENTITY % Script "CDATA" -- script expression -->
+
+<!ENTITY % StyleSheet "CDATA" -- style sheet data -->
+
+
+
+<!ENTITY % Text "CDATA">
+
+
+<!-- Parameter Entities -->
+
+<!ENTITY % head.misc "SCRIPT|STYLE|META|LINK|OBJECT" -- repeatable head elements -->
+
+<!ENTITY % heading "H1|H2|H3|H4|H5|H6">
+
+<!ENTITY % list "UL | OL">
+
+<!ENTITY % preformatted "PRE">
+
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin1//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLlat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLsymbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLspecial.ent">
+%HTMLspecial;
+<!--=================== Generic Attributes ===============================-->
+
+<!ENTITY % coreattrs
+ "id ID #IMPLIED -- document-wide unique id --
+ class CDATA #IMPLIED -- space separated list of classes --
+ style %StyleSheet; #IMPLIED -- associated style info --
+ title %Text; #IMPLIED -- advisory title/amplification --"
+ >
+
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #IMPLIED -- direction for weak/neutral text --"
+ >
+
+<!ENTITY % events
+ "onclick %Script; #IMPLIED -- a pointer button was clicked --
+ ondblclick %Script; #IMPLIED -- a pointer button was double clicked--
+ onmousedown %Script; #IMPLIED -- a pointer button was pressed down --
+ onmouseup %Script; #IMPLIED -- a pointer button was released --
+ onmouseover %Script; #IMPLIED -- a pointer was moved onto --
+ onmousemove %Script; #IMPLIED -- a pointer was moved within --
+ onmouseout %Script; #IMPLIED -- a pointer was moved away --
+ onkeypress %Script; #IMPLIED -- a key was pressed and released --
+ onkeydown %Script; #IMPLIED -- a key was pressed down --
+ onkeyup %Script; #IMPLIED -- a key was released --"
+ >
+
+<!-- Reserved Feature Switch -->
+<!ENTITY % HTML.Reserved "IGNORE">
+
+<!-- The following attributes are reserved for possible future use -->
+<![ %HTML.Reserved; [
+<!ENTITY % reserved
+ "datasrc %URI; #IMPLIED -- a single or tabular Data Source --
+ datafld CDATA #IMPLIED -- the property or column name --
+ dataformatas (plaintext|html) plaintext -- text or html --"
+ >
+]]>
+
+<!ENTITY % reserved "">
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+
+<!--=================== Text Markup ======================================-->
+
+<!ENTITY % fontstyle
+ "TT | I | B | BIG | SMALL">
+
+<!ENTITY % phrase "EM | STRONG | DFN | CODE |
+ SAMP | KBD | VAR | CITE | ABBR | ACRONYM" >
+
+<!ENTITY % special
+ "A | IMG | OBJECT | BR | SCRIPT | MAP | Q | SUB | SUP | SPAN | BDO">
+
+<!ENTITY % formctrl "INPUT | SELECT | TEXTAREA | LABEL | BUTTON">
+
+<!-- %inline; covers inline or "text-level" elements -->
+<!ENTITY % inline "#PCDATA | %fontstyle; | %phrase; | %special; | %formctrl;">
+
+<!ELEMENT (%fontstyle;|%phrase;) - - (%inline;)*>
+<!ATTLIST (%fontstyle;|%phrase;)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT (SUB|SUP) - - (%inline;)* -- subscript, superscript -->
+<!ATTLIST (SUB|SUP)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT SPAN - - (%inline;)* -- generic language/style container -->
+<!ATTLIST SPAN
+ %attrs; -- %coreattrs, %i18n, %events --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT BDO - - (%inline;)* -- I18N BiDi over-ride -->
+<!ATTLIST BDO
+ %coreattrs; -- id, class, style, title --
+ lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #REQUIRED -- directionality --
+ >
+
+
+<!ELEMENT BR - O EMPTY -- forced line break -->
+<!ATTLIST BR
+ %coreattrs; -- id, class, style, title --
+ >
+
+<!--================== HTML content models ===============================-->
+
+<!--
+ HTML has two basic content models:
+
+ %inline; character level elements and text strings
+ %block; block-like elements e.g. paragraphs and lists
+-->
+
+<!ENTITY % block
+ "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
+ BLOCKQUOTE | FORM | HR | TABLE | FIELDSET | ADDRESS">
+
+<!ENTITY % flow "%block; | %inline;">
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT BODY O O (%block;|SCRIPT)+ +(INS|DEL) -- document body -->
+<!ATTLIST BODY
+ %attrs; -- %coreattrs, %i18n, %events --
+ onload %Script; #IMPLIED -- the document has been loaded --
+ onunload %Script; #IMPLIED -- the document has been removed --
+ >
+
+<!ELEMENT ADDRESS - - (%inline;)* -- information on author -->
+<!ATTLIST ADDRESS
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT DIV - - (%flow;)* -- generic language/style container -->
+<!ATTLIST DIV
+ %attrs; -- %coreattrs, %i18n, %events --
+ %reserved; -- reserved for possible future use --
+ >
+
+
+<!--================== The Anchor Element ================================-->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+<!ENTITY % Coords "CDATA" -- comma separated list of lengths -->
+
+<!ELEMENT A - - (%inline;)* -(A) -- anchor -->
+<!ATTLIST A
+ %attrs; -- %coreattrs, %i18n, %events --
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ type %ContentType; #IMPLIED -- advisory content type --
+ name CDATA #IMPLIED -- named link end --
+ href %URI; #IMPLIED -- URI for linked resource --
+ hreflang %LanguageCode; #IMPLIED -- language code --
+ rel %LinkTypes; #IMPLIED -- forward link types --
+ rev %LinkTypes; #IMPLIED -- reverse link types --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ shape %Shape; rect -- for use with client-side image maps --
+ coords %Coords; #IMPLIED -- for use with client-side image maps --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT MAP - - ((%block;)+ | AREA+) -- client-side image map -->
+<!ATTLIST MAP
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #REQUIRED -- for reference by usemap --
+ >
+
+<!ELEMENT AREA - O EMPTY -- client-side image map area -->
+<!ATTLIST AREA
+ %attrs; -- %coreattrs, %i18n, %events --
+ shape %Shape; rect -- controls interpretation of coords --
+ coords %Coords; #IMPLIED -- comma separated list of lengths --
+ href %URI; #IMPLIED -- URI for linked resource --
+ nohref (nohref) #IMPLIED -- this region has no action --
+ alt %Text; #REQUIRED -- short description --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!--================== The LINK Element ==================================-->
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the LINK element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel=stylesheet)
+ c) to make a link to a script (rel=script)
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a postscript or pdf version (rel=alternate media=print)
+-->
+
+<!ELEMENT LINK - O EMPTY -- a media-independent link -->
+<!ATTLIST LINK
+ %attrs; -- %coreattrs, %i18n, %events --
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ href %URI; #IMPLIED -- URI for linked resource --
+ hreflang %LanguageCode; #IMPLIED -- language code --
+ type %ContentType; #IMPLIED -- advisory content type --
+ rel %LinkTypes; #IMPLIED -- forward link types --
+ rev %LinkTypes; #IMPLIED -- reverse link types --
+ media %MediaDesc; #IMPLIED -- for rendering on these media --
+ >
+
+<!--=================== Images ===========================================-->
+
+<!-- Length defined in strict DTD for cellpadding/cellspacing -->
+<!ENTITY % Length "CDATA" -- nn for pixels or nn% for percentage length -->
+<!ENTITY % MultiLength "CDATA" -- pixel, percentage, or relative -->
+
+<!ENTITY % MultiLengths "CDATA" -- comma-separated list of MultiLength -->
+
+<!ENTITY % Pixels "CDATA" -- integer representing length in pixels -->
+
+
+<!-- To avoid problems with text-only UAs as well as
+ to make image content understandable and navigable
+ to users of non-visual UAs, you need to provide
+ a description with ALT, and avoid server-side image maps -->
+<!ELEMENT IMG - O EMPTY -- Embedded image -->
+<!ATTLIST IMG
+ %attrs; -- %coreattrs, %i18n, %events --
+ src %URI; #REQUIRED -- URI of image to embed --
+ alt %Text; #REQUIRED -- short description --
+ longdesc %URI; #IMPLIED -- link to long description
+ (complements alt) --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ ismap (ismap) #IMPLIED -- use server-side image map --
+ >
+
+<!-- USEMAP points to a MAP element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--==================== OBJECT ======================================-->
+<!--
+ OBJECT is used to embed objects as part of HTML pages
+ PARAM elements should precede other content. SGML mixed content
+ model technicality precludes specifying this formally ...
+-->
+
+<!ELEMENT OBJECT - - (PARAM | %flow;)*
+ -- generic embedded object -->
+<!ATTLIST OBJECT
+ %attrs; -- %coreattrs, %i18n, %events --
+ declare (declare) #IMPLIED -- declare but don't instantiate flag --
+ classid %URI; #IMPLIED -- identifies an implementation --
+ codebase %URI; #IMPLIED -- base URI for classid, data, archive--
+ data %URI; #IMPLIED -- reference to object's data --
+ type %ContentType; #IMPLIED -- content type for data --
+ codetype %ContentType; #IMPLIED -- content type for code --
+ archive %URI; #IMPLIED -- space separated archive list --
+ standby %Text; #IMPLIED -- message to show while loading --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ name CDATA #IMPLIED -- submit as part of form --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT PARAM - O EMPTY -- named property value -->
+<!ATTLIST PARAM
+ id ID #IMPLIED -- document-wide unique id --
+ name CDATA #REQUIRED -- property name --
+ value CDATA #IMPLIED -- property value --
+ valuetype (DATA|REF|OBJECT) DATA -- How to interpret value --
+ type %ContentType; #IMPLIED -- content type for value
+ when valuetype=ref --
+ >
+
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT HR - O EMPTY -- horizontal rule -->
+<!ATTLIST HR
+ %coreattrs; -- id, class, style, title --
+ %events;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT P - O (%inline;)* -- paragraph -->
+<!ATTLIST P
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from H1 (the most important)
+ to H6 (the least important).
+-->
+
+<!ELEMENT (%heading;) - - (%inline;)* -- heading -->
+<!ATTLIST (%heading;)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- excludes markup for images and changes in font size -->
+<!ENTITY % pre.exclusion "IMG|OBJECT|BIG|SMALL|SUB|SUP">
+
+<!ELEMENT PRE - - (%inline;)* -(%pre.exclusion;) -- preformatted text -->
+<!ATTLIST PRE
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--===================== Inline Quotes ==================================-->
+
+<!ELEMENT Q - - (%inline;)* -- short inline quotation -->
+<!ATTLIST Q
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- URI for source document or msg --
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT BLOCKQUOTE - - (%block;|SCRIPT)+ -- long quotation -->
+<!ATTLIST BLOCKQUOTE
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- URI for source document or msg --
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+
+<!-- INS/DEL are handled by inclusion on BODY -->
+<!ELEMENT (INS|DEL) - - (%flow;)* -- inserted text, deleted text -->
+<!ATTLIST (INS|DEL)
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- info on reason for change --
+ datetime %Datetime; #IMPLIED -- date and time of change --
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- definition lists - DT for term, DD for its definition -->
+
+<!ELEMENT DL - - (DT|DD)+ -- definition list -->
+<!ATTLIST DL
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT DT - O (%inline;)* -- definition term -->
+<!ELEMENT DD - O (%flow;)* -- definition description -->
+<!ATTLIST (DT|DD)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+
+<!ELEMENT OL - - (LI)+ -- ordered list -->
+<!ATTLIST OL
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!-- Unordered Lists (UL) bullet styles -->
+<!ELEMENT UL - - (LI)+ -- unordered list -->
+<!ATTLIST UL
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+
+
+<!ELEMENT LI - O (%flow;)* -- list item -->
+<!ATTLIST LI
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--================ Forms ===============================================-->
+<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->
+<!ATTLIST FORM
+ %attrs; -- %coreattrs, %i18n, %events --
+ action %URI; #REQUIRED -- server-side form handler --
+ method (GET|POST) GET -- HTTP method used to submit the form--
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED -- the form was submitted --
+ onreset %Script; #IMPLIED -- the form was reset --
+ accept-charset %Charsets; #IMPLIED -- list of supported charsets --
+ >
+
+<!-- Each label must not contain more than ONE field -->
+<!ELEMENT LABEL - - (%inline;)* -(LABEL) -- form field label text -->
+<!ATTLIST LABEL
+ %attrs; -- %coreattrs, %i18n, %events --
+ for IDREF #IMPLIED -- matches field ID value --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!ENTITY % InputType
+ "(TEXT | PASSWORD | CHECKBOX |
+ RADIO | SUBMIT | RESET |
+ FILE | HIDDEN | IMAGE | BUTTON)"
+ >
+
+<!-- attribute name required for all but submit & reset -->
+<!ELEMENT INPUT - O EMPTY -- form control -->
+<!ATTLIST INPUT
+ %attrs; -- %coreattrs, %i18n, %events --
+ type %InputType; TEXT -- what kind of widget is needed --
+ name CDATA #IMPLIED -- submit as part of form --
+ value CDATA #IMPLIED -- required for radio and checkboxes --
+ checked (checked) #IMPLIED -- for radio buttons and check boxes --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ readonly (readonly) #IMPLIED -- for text and passwd --
+ size CDATA #IMPLIED -- specific to each type of field --
+ maxlength NUMBER #IMPLIED -- max chars for text fields --
+ src %URI; #IMPLIED -- for fields with images --
+ alt CDATA #IMPLIED -- short description --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onselect %Script; #IMPLIED -- some text was selected --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ accept %ContentTypes; #IMPLIED -- list of MIME types for file upload --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT SELECT - - (OPTGROUP|OPTION)+ -- option selector -->
+<!ATTLIST SELECT
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED -- field name --
+ size NUMBER #IMPLIED -- rows visible --
+ multiple (multiple) #IMPLIED -- default is single selection --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT OPTGROUP - - (OPTION)+ -- option group -->
+<!ATTLIST OPTGROUP
+ %attrs; -- %coreattrs, %i18n, %events --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ label %Text; #REQUIRED -- for use in hierarchical menus --
+ >
+
+<!ELEMENT OPTION - O (#PCDATA) -- selectable choice -->
+<!ATTLIST OPTION
+ %attrs; -- %coreattrs, %i18n, %events --
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ label %Text; #IMPLIED -- for use in hierarchical menus --
+ value CDATA #IMPLIED -- defaults to element content --
+ >
+
+<!ELEMENT TEXTAREA - - (#PCDATA) -- multi-line text field -->
+<!ATTLIST TEXTAREA
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED
+ rows NUMBER #REQUIRED
+ cols NUMBER #REQUIRED
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ readonly (readonly) #IMPLIED
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onselect %Script; #IMPLIED -- some text was selected --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!--
+ #PCDATA is to solve the mixed content problem,
+ per specification only whitespace is allowed there!
+ -->
+<!ELEMENT FIELDSET - - (#PCDATA,LEGEND,(%flow;)*) -- form control group -->
+<!ATTLIST FIELDSET
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT LEGEND - - (%inline;)* -- fieldset legend -->
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ATTLIST LEGEND
+ %attrs; -- %coreattrs, %i18n, %events --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ >
+
+<!ELEMENT BUTTON - -
+ (%flow;)* -(A|%formctrl;|FORM|FIELDSET)
+ -- push button -->
+<!ATTLIST BUTTON
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED -- sent to server when submitted --
+ type (button|submit|reset) submit -- for use as form button --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The BORDER attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The FRAME attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the VALIGN attribute.
+
+ The value "border" is included for backwards compatibility with
+ <TABLE BORDER> which yields frame=border and border=implied
+ For <TABLE BORDER=1> you get border=1 and frame=implied. In this
+ case, it is appropriate to treat this as frame=border for backwards
+ compatibility with deployed browsers.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The RULES attribute defines which rules to draw between cells:
+
+ If RULES is absent then assume:
+ "none" if BORDER is absent or BORDER=0 otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents -->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED -- alignment char, e.g. char=':' --
+ charoff %Length; #IMPLIED -- offset for alignment char --"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT TABLE - -
+ (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)>
+<!ELEMENT CAPTION - - (%inline;)* -- table caption -->
+<!ELEMENT THEAD - O (TR)+ -- table header -->
+<!ELEMENT TFOOT - O (TR)+ -- table footer -->
+<!ELEMENT TBODY O O (TR)+ -- table body -->
+<!ELEMENT COLGROUP - O (col)* -- table column group -->
+<!ELEMENT COL - O EMPTY -- table column -->
+<!ELEMENT TR - O (TH|TD)+ -- table row -->
+<!ELEMENT (TH|TD) - O (%flow;)* -- table header cell, table data cell-->
+
+<!ATTLIST TABLE -- table element --
+ %attrs; -- %coreattrs, %i18n, %events --
+ summary %Text; #IMPLIED -- purpose/structure for speech output--
+ width %Length; #IMPLIED -- table width --
+ border %Pixels; #IMPLIED -- controls frame width around table --
+ frame %TFrame; #IMPLIED -- which parts of frame to render --
+ rules %TRules; #IMPLIED -- rulings between rows and cols --
+ cellspacing %Length; #IMPLIED -- spacing between cells --
+ cellpadding %Length; #IMPLIED -- spacing within cells --
+ %reserved; -- reserved for possible future use --
+ datapagesize CDATA #IMPLIED -- reserved for possible future use --
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST CAPTION
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--
+COLGROUP groups a set of COL elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST COLGROUP
+ %attrs; -- %coreattrs, %i18n, %events --
+ span NUMBER 1 -- default number of columns in group --
+ width %MultiLength; #IMPLIED -- default width for enclosed COLs --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!--
+ COL elements define the alignment properties for cells in
+ one or more columns.
+
+ The WIDTH attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The SPAN attribute causes the attributes of one
+ COL element to apply to more than one column.
+-->
+<!ATTLIST COL -- column groups and properties --
+ %attrs; -- %coreattrs, %i18n, %events --
+ span NUMBER 1 -- COL attributes affect N columns --
+ width %MultiLength; #IMPLIED -- column width specification --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!--
+ Use THEAD to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ TBODY sections are rendered in scrolling panel.
+
+ Use TFOOT to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ TBODY sections are rendered in scrolling panel.
+
+ Use multiple TBODY sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST (THEAD|TBODY|TFOOT) -- table section --
+ %attrs; -- %coreattrs, %i18n, %events --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!ATTLIST TR -- table row --
+ %attrs; -- %coreattrs, %i18n, %events --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+
+<!-- Scope is simpler than axes attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- TH is for headers, TD for data, but for cells acting as both use TD -->
+<!ATTLIST (TH|TD) -- header or data cell --
+ %attrs; -- %coreattrs, %i18n, %events --
+ abbr %Text; #IMPLIED -- abbreviation for header cell --
+ axis CDATA #IMPLIED -- names groups of related headers--
+ headers IDREFS #IMPLIED -- list of id's for header cells --
+ scope %Scope; #IMPLIED -- scope covered by header cells --
+ rowspan NUMBER 1 -- number of rows spanned by cell --
+ colspan NUMBER 1 -- number of cols spanned by cell --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+
+<!--================ Document Head =======================================-->
+<!-- %head.misc; defined earlier on as "SCRIPT|STYLE|META|LINK|OBJECT" -->
+<!ENTITY % head.content "TITLE & BASE?">
+
+<!ELEMENT HEAD O O (%head.content;) +(%head.misc;) -- document head -->
+<!ATTLIST HEAD
+ %i18n; -- lang, dir --
+ profile %URI; #IMPLIED -- named dictionary of meta info --
+ >
+
+<!-- The TITLE element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT TITLE - - (#PCDATA) -(%head.misc;) -- document title -->
+<!ATTLIST TITLE %i18n>
+
+
+<!ELEMENT BASE - O EMPTY -- document base URI -->
+<!ATTLIST BASE
+ href %URI; #REQUIRED -- URI that acts as base URI --
+ >
+
+<!ELEMENT META - O EMPTY -- generic metainformation -->
+<!ATTLIST META
+ %i18n; -- lang, dir, for use with content --
+ http-equiv NAME #IMPLIED -- HTTP response header name --
+ name NAME #IMPLIED -- metainformation name --
+ content CDATA #REQUIRED -- associated information --
+ scheme CDATA #IMPLIED -- select form of content --
+ >
+
+<!ELEMENT STYLE - - %StyleSheet -- style info -->
+<!ATTLIST STYLE
+ %i18n; -- lang, dir, for use with title --
+ type %ContentType; #REQUIRED -- content type of style language --
+ media %MediaDesc; #IMPLIED -- designed for use with these media --
+ title %Text; #IMPLIED -- advisory title --
+ >
+
+<!ELEMENT SCRIPT - - %Script; -- script statements -->
+<!ATTLIST SCRIPT
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ type %ContentType; #REQUIRED -- content type of script language --
+ language CDATA #IMPLIED -- predefined script language name --
+ src %URI; #IMPLIED -- URI for an external script --
+ defer (defer) #IMPLIED -- UA may defer execution of script --
+ event CDATA #IMPLIED -- reserved for possible future use --
+ for %URI; #IMPLIED -- reserved for possible future use --
+ >
+
+<!ELEMENT NOSCRIPT - - (%block;)+
+ -- alternate content container for non script-based rendering -->
+<!ATTLIST NOSCRIPT
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--================ Document Structure ==================================-->
+<!ENTITY % html.content "HEAD, BODY">
+
+<!ELEMENT HTML O O (%html.content;) -- document root element -->
+<!ATTLIST HTML
+ %i18n; -- lang, dir --
+ >
diff --git a/tests/dtds/HTML4.dcl b/tests/dtds/HTML4.dcl
new file mode 100644
index 0000000..db46db0
--- /dev/null
+++ b/tests/dtds/HTML4.dcl
@@ -0,0 +1,88 @@
+<!SGML "ISO 8879:1986 (WWW)"
+ --
+ SGML Declaration for HyperText Markup Language version 4.0
+
+ With support for the first 17 planes of ISO 10646 and
+ increased limits for tag and literal lengths etc.
+
+ Modified by jjc to work around SP's 16-bit character limit.
+ Modified by jjc to support hex character references.
+ --
+
+ CHARSET
+ BASESET "ISO Registration Number 177//CHARSET
+ ISO/IEC 10646-1:1993 UCS-4 with
+ implementation level 3//ESC 2/5 2/15 4/6"
+ DESCSET 0 9 UNUSED
+ 9 2 9
+ 11 2 UNUSED
+ 13 1 13
+ 14 18 UNUSED
+ 32 95 32
+ 127 1 UNUSED
+ 128 32 UNUSED
+ -- jjc: changed the rest of the DESCSET.
+ Note that surrogates are not declared UNUSED;
+ this allows non-BMP characters to be parsed. --
+ 160 65376 160
+ -- 160 55136 160
+ 55296 2048 UNUSED
+ 57344 1056768 57344 --
+
+CAPACITY SGMLREF
+ TOTALCAP 150000
+ GRPCAP 150000
+ ENTCAP 150000
+
+SCOPE DOCUMENT
+SYNTAX
+ SHUNCHAR CONTROLS 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 127
+ BASESET "ISO 646IRV:1991//CHARSET
+ International Reference Version
+ (IRV)//ESC 2/8 4/2"
+ DESCSET 0 128 0
+
+ FUNCTION
+ RE 13
+ RS 10
+ SPACE 32
+ TAB SEPCHAR 9
+
+ NAMING LCNMSTRT ""
+ UCNMSTRT ""
+ LCNMCHAR ".-_:"
+ UCNMCHAR ".-_:"
+ NAMECASE GENERAL YES
+ ENTITY NO
+ DELIM GENERAL SGMLREF
+ HCRO "&#38;#X" -- added by jjc --
+ SHORTREF SGMLREF
+ NAMES SGMLREF
+ QUANTITY SGMLREF
+ ATTCNT 60 -- increased --
+ ATTSPLEN 65536 -- These are the largest values --
+ LITLEN 65536 -- permitted in the declaration --
+ NAMELEN 65536 -- Avoid fixed limits in actual --
+ PILEN 65536 -- implementations of HTML UA's --
+ TAGLVL 100
+ TAGLEN 65536
+ GRPGTCNT 150
+ GRPCNT 64
+
+FEATURES
+ MINIMIZE
+ DATATAG NO
+ OMITTAG YES
+ RANK NO
+ SHORTTAG YES
+ LINK
+ SIMPLE NO
+ IMPLICIT NO
+ EXPLICIT NO
+ OTHER
+ CONCUR NO
+ SUBDOC NO
+ FORMAL YES
+ APPINFO NONE
+> \ No newline at end of file
diff --git a/tests/dtds/HTML4.dtd b/tests/dtds/HTML4.dtd
new file mode 100644
index 0000000..9e781db
--- /dev/null
+++ b/tests/dtds/HTML4.dtd
@@ -0,0 +1,1092 @@
+<!--
+ This is the HTML 4.0 Transitional DTD, which includes
+ presentation attributes and elements that W3C expects to phase out
+ as support for style sheets matures. Authors should use the Strict
+ DTD when possible, but may use the Transitional DTD when support
+ for presentation attribute and elements is required.
+
+ HTML 4.0 includes mechanisms for style sheets, scripting,
+ embedding objects, improved support for right to left and mixed
+ direction text, and enhancements to forms for improved
+ accessibility for people with disabilities.
+
+ Draft: $Date: 1999/05/02 15:37:15 $
+
+ Authors:
+ Dave Raggett <dsr@w3.org>
+ Arnaud Le Hors <lehors@w3.org>
+ Ian Jacobs <ij@w3.org>
+
+ Further information about HTML 4.0 is available at:
+
+ http://www.w3.org/TR/REC-html40
+-->
+<!ENTITY % HTML.Version "-//W3C//DTD HTML 4.0 Transitional//EN"
+ -- Typical usage:
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/REC-html40/loose.dtd">
+ <html>
+ <head>
+ ...
+ </head>
+ <body>
+ ...
+ </body>
+ </html>
+
+ The URI used as a system identifier with the public identifier allows
+ the user agent to download the DTD and entity sets as needed.
+
+ The FPI for the Strict HTML 4.0 DTD is:
+
+ "-//W3C//DTD HTML 4.0//EN"
+
+ and its URI is:
+
+ http://www.w3.org/TR/REC-html40/strict.dtd
+
+ Authors should use the Strict DTD unless they need the
+ presentation control for user agents that don't (adequately)
+ support style sheets.
+
+ If you are writing a document that includes frames, use
+ the following FPI:
+
+ "-//W3C//DTD HTML 4.0 Frameset//EN"
+
+ with the URI:
+
+ http://www.w3.org/TR/REC-html40/frameset.dtd
+
+ The following URIs are supported in relation to HTML 4.0
+
+ "http://www.w3.org/TR/REC-html40/strict.dtd" (Strict DTD)
+ "http://www.w3.org/TR/REC-html40/loose.dtd" (Loose DTD)
+ "http://www.w3.org/TR/REC-html40/frameset.dtd" (Frameset DTD)
+ "http://www.w3.org/TR/REC-html40/HTMLlat1.ent" (Latin-1 entities)
+ "http://www.w3.org/TR/REC-html40/HTMLsymbol.ent" (Symbol entities)
+ "http://www.w3.org/TR/REC-html40/HTMLspecial.ent" (Special entities)
+
+ These URIs point to the latest version of each file. To reference
+ this specific revision use the following URIs:
+
+ "http://www.w3.org/TR/REC-html40-971218/strict.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/loose.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/frameset.dtd"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLlat1.ent"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLsymbol.ent"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLspecial.ent"
+
+-->
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA"
+ -- media type, as per [RFC2045]
+ -->
+
+<!ENTITY % ContentTypes "CDATA"
+ -- comma-separated list of media types, as per [RFC2045]
+ -->
+
+<!ENTITY % Charset "CDATA"
+ -- a character encoding, as per [RFC2045]
+ -->
+
+<!ENTITY % Charsets "CDATA"
+ -- a space separated list of character encodings, as per [RFC2045]
+ -->
+
+<!ENTITY % LanguageCode "NAME"
+ -- a language code, as per [RFC1766]
+ -->
+
+<!ENTITY % Character "CDATA"
+ -- a single character from [ISO10646]
+ -->
+
+<!ENTITY % LinkTypes "CDATA"
+ -- space-separated list of link types
+ -->
+
+<!ENTITY % MediaDesc "CDATA"
+ -- single or comma-separated list of media descriptors
+ -->
+
+<!ENTITY % URI "CDATA"
+ -- a Uniform Resource Identifier,
+ see [URI]
+ -->
+
+<!ENTITY % Datetime "CDATA" -- date and time information. ISO date format -->
+
+
+<!ENTITY % Script "CDATA" -- script expression -->
+
+<!ENTITY % StyleSheet "CDATA" -- style sheet data -->
+
+<!ENTITY % FrameTarget "CDATA" -- render in this frame -->
+
+
+<!ENTITY % Text "CDATA">
+
+
+<!-- Parameter Entities -->
+
+<!ENTITY % head.misc "SCRIPT|STYLE|META|LINK|OBJECT" -- repeatable head elements -->
+
+<!ENTITY % heading "H1|H2|H3|H4|H5|H6">
+
+<!ENTITY % list "UL | OL | DIR | MENU">
+
+<!ENTITY % preformatted "PRE">
+
+<!ENTITY % Color "CDATA" -- a color using sRGB: #RRGGBB as Hex values -->
+
+<!-- There are also 16 widely known color names with their sRGB values:
+
+ Black = #000000 Green = #008000
+ Silver = #C0C0C0 Lime = #00FF00
+ Gray = #808080 Olive = #808000
+ White = #FFFFFF Yellow = #FFFF00
+ Maroon = #800000 Navy = #000080
+ Red = #FF0000 Blue = #0000FF
+ Purple = #800080 Teal = #008080
+ Fuchsia= #FF00FF Aqua = #00FFFF
+ -->
+
+<!ENTITY % bodycolors "
+ bgcolor %Color; #IMPLIED -- document background color --
+ text %Color; #IMPLIED -- document text color --
+ link %Color; #IMPLIED -- color of links --
+ vlink %Color; #IMPLIED -- color of visited links --
+ alink %Color; #IMPLIED -- color of selected links --
+ ">
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin1//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLlat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLsymbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special//EN//HTML"
+ "http://www.w3.org/TR/REC-html40-971218/HTMLspecial.ent">
+%HTMLspecial;
+<!--=================== Generic Attributes ===============================-->
+
+<!ENTITY % coreattrs
+ "id ID #IMPLIED -- document-wide unique id --
+ class CDATA #IMPLIED -- space separated list of classes --
+ style %StyleSheet; #IMPLIED -- associated style info --
+ title %Text; #IMPLIED -- advisory title/amplification --"
+ >
+
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #IMPLIED -- direction for weak/neutral text --"
+ >
+
+<!ENTITY % events
+ "onclick %Script; #IMPLIED -- a pointer button was clicked --
+ ondblclick %Script; #IMPLIED -- a pointer button was double clicked--
+ onmousedown %Script; #IMPLIED -- a pointer button was pressed down --
+ onmouseup %Script; #IMPLIED -- a pointer button was released --
+ onmouseover %Script; #IMPLIED -- a pointer was moved onto --
+ onmousemove %Script; #IMPLIED -- a pointer was moved within --
+ onmouseout %Script; #IMPLIED -- a pointer was moved away --
+ onkeypress %Script; #IMPLIED -- a key was pressed and released --
+ onkeydown %Script; #IMPLIED -- a key was pressed down --
+ onkeyup %Script; #IMPLIED -- a key was released --"
+ >
+
+<!-- Reserved Feature Switch -->
+<!ENTITY % HTML.Reserved "IGNORE">
+
+<!-- The following attributes are reserved for possible future use -->
+<![ %HTML.Reserved; [
+<!ENTITY % reserved
+ "datasrc %URI; #IMPLIED -- a single or tabular Data Source --
+ datafld CDATA #IMPLIED -- the property or column name --
+ dataformatas (plaintext|html) plaintext -- text or html --"
+ >
+]]>
+
+<!ENTITY % reserved "">
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!ENTITY % align "align (left|center|right|justify) #IMPLIED"
+ -- default is left for ltr paragraphs, right for rtl --
+ >
+
+<!--=================== Text Markup ======================================-->
+
+<!ENTITY % fontstyle
+ "TT | I | B | U | S | STRIKE | BIG | SMALL">
+
+<!ENTITY % phrase "EM | STRONG | DFN | CODE |
+ SAMP | KBD | VAR | CITE | ABBR | ACRONYM" >
+
+<!ENTITY % special
+ "A | IMG | APPLET | OBJECT | FONT | BASEFONT | BR | SCRIPT |
+ MAP | Q | SUB | SUP | SPAN | BDO | IFRAME">
+
+<!ENTITY % formctrl "INPUT | SELECT | TEXTAREA | LABEL | BUTTON">
+
+<!-- %inline; covers inline or "text-level" elements -->
+<!ENTITY % inline "#PCDATA | %fontstyle; | %phrase; | %special; | %formctrl;">
+
+<!ELEMENT (%fontstyle;|%phrase;) - - (%inline;)*>
+<!ATTLIST (%fontstyle;|%phrase;)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT (SUB|SUP) - - (%inline;)* -- subscript, superscript -->
+<!ATTLIST (SUB|SUP)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT SPAN - - (%inline;)* -- generic language/style container -->
+<!ATTLIST SPAN
+ %attrs; -- %coreattrs, %i18n, %events --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT BDO - - (%inline;)* -- I18N BiDi over-ride -->
+<!ATTLIST BDO
+ %coreattrs; -- id, class, style, title --
+ lang %LanguageCode; #IMPLIED -- language code --
+ dir (ltr|rtl) #REQUIRED -- directionality --
+ >
+
+<!ELEMENT BASEFONT - O EMPTY -- base font size -->
+<!ATTLIST BASEFONT
+ id ID #IMPLIED -- document-wide unique id --
+ size CDATA #REQUIRED -- base font size for FONT elements --
+ color %Color; #IMPLIED -- text color --
+ face CDATA #IMPLIED -- comma separated list of font names --
+ >
+
+<!ELEMENT FONT - - (%inline;)* -- local change to font -->
+<!ATTLIST FONT
+ %coreattrs; -- id, class, style, title --
+ %i18n; -- lang, dir --
+ size CDATA #IMPLIED -- [+|-]nn e.g. size="+1", size="4" --
+ color %Color; #IMPLIED -- text color --
+ face CDATA #IMPLIED -- comma separated list of font names --
+ >
+
+<!ELEMENT BR - O EMPTY -- forced line break -->
+<!ATTLIST BR
+ %coreattrs; -- id, class, style, title --
+ clear (left|all|right|none) none -- control of text flow --
+ >
+
+<!--================== HTML content models ===============================-->
+
+<!--
+ HTML has two basic content models:
+
+ %inline; character level elements and text strings
+ %block; block-like elements e.g. paragraphs and lists
+-->
+
+<!ENTITY % block
+ "P | %heading; | %list; | %preformatted; | DL | DIV | CENTER |
+ NOSCRIPT | NOFRAMES | BLOCKQUOTE | FORM | ISINDEX | HR |
+ TABLE | FIELDSET | ADDRESS">
+
+<!ENTITY % flow "%block; | %inline;">
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT BODY O O (%flow;)* +(INS|DEL) -- document body -->
+<!ATTLIST BODY
+ %attrs; -- %coreattrs, %i18n, %events --
+ onload %Script; #IMPLIED -- the document has been loaded --
+ onunload %Script; #IMPLIED -- the document has been removed --
+ background %URI; #IMPLIED -- texture tile for document
+ background --
+ %bodycolors; -- bgcolor, text, link, vlink, alink --
+ >
+
+<!ELEMENT ADDRESS - - ((%inline;)|P)* -- information on author -->
+<!ATTLIST ADDRESS
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT DIV - - (%flow;)* -- generic language/style container -->
+<!ATTLIST DIV
+ %attrs; -- %coreattrs, %i18n, %events --
+ %align; -- align, text alignment --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT CENTER - - (%flow;)* -- shorthand for DIV align=center -->
+<!ATTLIST CENTER
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+<!ENTITY % Coords "CDATA" -- comma separated list of lengths -->
+
+<!ELEMENT A - - (%inline;)* -(A) -- anchor -->
+<!ATTLIST A
+ %attrs; -- %coreattrs, %i18n, %events --
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ type %ContentType; #IMPLIED -- advisory content type --
+ name CDATA #IMPLIED -- named link end --
+ href %URI; #IMPLIED -- URI for linked resource --
+ hreflang %LanguageCode; #IMPLIED -- language code --
+ target %FrameTarget; #IMPLIED -- render in this frame --
+ rel %LinkTypes; #IMPLIED -- forward link types --
+ rev %LinkTypes; #IMPLIED -- reverse link types --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ shape %Shape; rect -- for use with client-side image maps --
+ coords %Coords; #IMPLIED -- for use with client-side image maps --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT MAP - - ((%block;)+ | AREA+) -- client-side image map -->
+<!ATTLIST MAP
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #REQUIRED -- for reference by usemap --
+ >
+
+<!ELEMENT AREA - O EMPTY -- client-side image map area -->
+<!ATTLIST AREA
+ %attrs; -- %coreattrs, %i18n, %events --
+ shape %Shape; rect -- controls interpretation of coords --
+ coords %Coords; #IMPLIED -- comma separated list of lengths --
+ href %URI; #IMPLIED -- URI for linked resource --
+ target %FrameTarget; #IMPLIED -- render in this frame --
+ nohref (nohref) #IMPLIED -- this region has no action --
+ alt %Text; #REQUIRED -- short description --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!--================== The LINK Element ==================================-->
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the LINK element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel=stylesheet)
+ c) to make a link to a script (rel=script)
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a postscript or pdf version (rel=alternate media=print)
+-->
+
+<!ELEMENT LINK - O EMPTY -- a media-independent link -->
+<!ATTLIST LINK
+ %attrs; -- %coreattrs, %i18n, %events --
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ href %URI; #IMPLIED -- URI for linked resource --
+ hreflang %LanguageCode; #IMPLIED -- language code --
+ type %ContentType; #IMPLIED -- advisory content type --
+ rel %LinkTypes; #IMPLIED -- forward link types --
+ rev %LinkTypes; #IMPLIED -- reverse link types --
+ media %MediaDesc; #IMPLIED -- for rendering on these media --
+ target %FrameTarget; #IMPLIED -- render in this frame --
+ >
+
+<!--=================== Images ===========================================-->
+
+<!-- Length defined in strict DTD for cellpadding/cellspacing -->
+<!ENTITY % Length "CDATA" -- nn for pixels or nn% for percentage length -->
+<!ENTITY % MultiLength "CDATA" -- pixel, percentage, or relative -->
+
+<!ENTITY % MultiLengths "CDATA" -- comma-separated list of MultiLength -->
+
+<!ENTITY % Pixels "CDATA" -- integer representing length in pixels -->
+
+<!ENTITY % IAlign "(top|middle|bottom|left|right)" -- center? -->
+
+<!-- To avoid problems with text-only UAs as well as
+ to make image content understandable and navigable
+ to users of non-visual UAs, you need to provide
+ a description with ALT, and avoid server-side image maps -->
+<!ELEMENT IMG - O EMPTY -- Embedded image -->
+<!ATTLIST IMG
+ %attrs; -- %coreattrs, %i18n, %events --
+ src %URI; #REQUIRED -- URI of image to embed --
+ alt %Text; #REQUIRED -- short description --
+ longdesc %URI; #IMPLIED -- link to long description
+ (complements alt) --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ ismap (ismap) #IMPLIED -- use server-side image map --
+ align %IAlign; #IMPLIED -- vertical or horizontal alignment --
+ border %Length; #IMPLIED -- link border width --
+ hspace %Pixels; #IMPLIED -- horizontal gutter --
+ vspace %Pixels; #IMPLIED -- vertical gutter --
+ >
+
+<!-- USEMAP points to a MAP element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--==================== OBJECT ======================================-->
+<!--
+ OBJECT is used to embed objects as part of HTML pages
+ PARAM elements should precede other content. SGML mixed content
+ model technicality precludes specifying this formally ...
+-->
+
+<!ELEMENT OBJECT - - (PARAM | %flow;)*
+ -- generic embedded object -->
+<!ATTLIST OBJECT
+ %attrs; -- %coreattrs, %i18n, %events --
+ declare (declare) #IMPLIED -- declare but don't instantiate flag --
+ classid %URI; #IMPLIED -- identifies an implementation --
+ codebase %URI; #IMPLIED -- base URI for classid, data, archive--
+ data %URI; #IMPLIED -- reference to object's data --
+ type %ContentType; #IMPLIED -- content type for data --
+ codetype %ContentType; #IMPLIED -- content type for code --
+ archive %URI; #IMPLIED -- space separated archive list --
+ standby %Text; #IMPLIED -- message to show while loading --
+ height %Length; #IMPLIED -- override height --
+ width %Length; #IMPLIED -- override width --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ name CDATA #IMPLIED -- submit as part of form --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ align %IAlign; #IMPLIED -- vertical or horizontal alignment --
+ border %Length; #IMPLIED -- link border width --
+ hspace %Pixels; #IMPLIED -- horizontal gutter --
+ vspace %Pixels; #IMPLIED -- vertical gutter --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT PARAM - O EMPTY -- named property value -->
+<!ATTLIST PARAM
+ id ID #IMPLIED -- document-wide unique id --
+ name CDATA #REQUIRED -- property name --
+ value CDATA #IMPLIED -- property value --
+ valuetype (DATA|REF|OBJECT) DATA -- How to interpret value --
+ type %ContentType; #IMPLIED -- content type for value
+ when valuetype=ref --
+ >
+
+<!--=================== Java APPLET ==================================-->
+<!--
+ One of code or object attributes must be present.
+ Place PARAM elements before other content.
+-->
+<!ELEMENT APPLET - - (PARAM | %flow;)* -- Java applet -->
+<!ATTLIST APPLET
+ %coreattrs; -- id, class, style, title --
+ codebase %URI; #IMPLIED -- optional base URI for applet --
+ archive CDATA #IMPLIED -- comma separated archive list --
+ code CDATA #IMPLIED -- applet class file --
+ object CDATA #IMPLIED -- serialized applet file --
+ alt %Text; #IMPLIED -- short description --
+ name CDATA #IMPLIED -- allows applets to find each other --
+ width %Length; #REQUIRED -- initial width --
+ height %Length; #REQUIRED -- initial height --
+ align %IAlign; #IMPLIED -- vertical or horizontal alignment --
+ hspace %Pixels; #IMPLIED -- horizontal gutter --
+ vspace %Pixels; #IMPLIED -- vertical gutter --
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT HR - O EMPTY -- horizontal rule -->
+<!ATTLIST HR
+ %coreattrs; -- id, class, style, title --
+ %events;
+ align (left|center|right) #IMPLIED
+ noshade (noshade) #IMPLIED
+ size %Pixels; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT P - O (%inline;)* -- paragraph -->
+<!ATTLIST P
+ %attrs; -- %coreattrs, %i18n, %events --
+ %align; -- align, text alignment --
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from H1 (the most important)
+ to H6 (the least important).
+-->
+
+<!ELEMENT (%heading;) - - (%inline;)* -- heading -->
+<!ATTLIST (%heading;)
+ %attrs; -- %coreattrs, %i18n, %events --
+ %align; -- align, text alignment --
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- excludes markup for images and changes in font size -->
+<!ENTITY % pre.exclusion "IMG|OBJECT|APPLET|BIG|SMALL|SUB|SUP|FONT|BASEFONT">
+
+<!ELEMENT PRE - - (%inline;)* -(%pre.exclusion;) -- preformatted text -->
+<!ATTLIST PRE
+ %attrs; -- %coreattrs, %i18n, %events --
+ width NUMBER #IMPLIED
+ >
+
+<!--===================== Inline Quotes ==================================-->
+
+<!ELEMENT Q - - (%inline;)* -- short inline quotation -->
+<!ATTLIST Q
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- URI for source document or msg --
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT BLOCKQUOTE - - (%flow;)* -- long quotation -->
+<!ATTLIST BLOCKQUOTE
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- URI for source document or msg --
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+
+<!-- INS/DEL are handled by inclusion on BODY -->
+<!ELEMENT (INS|DEL) - - (%flow;)* -- inserted text, deleted text -->
+<!ATTLIST (INS|DEL)
+ %attrs; -- %coreattrs, %i18n, %events --
+ cite %URI; #IMPLIED -- info on reason for change --
+ datetime %Datetime; #IMPLIED -- date and time of change --
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- definition lists - DT for term, DD for its definition -->
+
+<!ELEMENT DL - - (DT|DD)+ -- definition list -->
+<!ATTLIST DL
+ %attrs; -- %coreattrs, %i18n, %events --
+ compact (compact) #IMPLIED -- reduced interitem spacing --
+ >
+
+<!ELEMENT DT - O (%inline;)* -- definition term -->
+<!ELEMENT DD - O (%flow;)* -- definition description -->
+<!ATTLIST (DT|DD)
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!-- Ordered lists (OL) Numbering style
+
+ 1 arablic numbers 1, 2, 3, ...
+ a lower alpha a, b, c, ...
+ A upper alpha A, B, C, ...
+ i lower roman i, ii, iii, ...
+ I upper roman I, II, III, ...
+
+ The style is applied to the sequence number which by default
+ is reset to 1 for the first list item in an ordered list.
+
+ This can't be expressed directly in SGML due to case folding.
+-->
+
+<!ENTITY % OLStyle "CDATA" -- constrained to: "(1|a|A|i|I)" -->
+
+<!ELEMENT OL - - (LI)+ -- ordered list -->
+<!ATTLIST OL
+ %attrs; -- %coreattrs, %i18n, %events --
+ type %OLStyle; #IMPLIED -- numbering style --
+ compact (compact) #IMPLIED -- reduced interitem spacing --
+ start NUMBER #IMPLIED -- starting sequence number --
+ >
+
+<!-- Unordered Lists (UL) bullet styles -->
+<!ENTITY % ULStyle "(disc|square|circle)">
+
+<!ELEMENT UL - - (LI)+ -- unordered list -->
+<!ATTLIST UL
+ %attrs; -- %coreattrs, %i18n, %events --
+ type %ULStyle; #IMPLIED -- bullet style --
+ compact (compact) #IMPLIED -- reduced interitem spacing --
+ >
+
+<!ELEMENT (DIR|MENU) - - (LI)+ -(%block;) -- directory list, menu list -->
+<!ATTLIST DIR
+ %attrs; -- %coreattrs, %i18n, %events --
+ compact (compact) #IMPLIED
+ >
+<!ATTLIST MENU
+ %attrs; -- %coreattrs, %i18n, %events --
+ compact (compact) #IMPLIED
+ >
+
+<!ENTITY % LIStyle "CDATA" -- constrained to: "(%ULStyle;|%OLStyle;)" -->
+
+<!ELEMENT LI - O (%flow;)* -- list item -->
+<!ATTLIST LI
+ %attrs; -- %coreattrs, %i18n, %events --
+ type %LIStyle; #IMPLIED -- list item style --
+ value NUMBER #IMPLIED -- reset sequence number --
+ >
+
+<!--================ Forms ===============================================-->
+<!ELEMENT FORM - - (%flow;)* -(FORM) -- interactive form -->
+<!ATTLIST FORM
+ %attrs; -- %coreattrs, %i18n, %events --
+ action %URI; #REQUIRED -- server-side form handler --
+ method (GET|POST) GET -- HTTP method used to submit the form--
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED -- the form was submitted --
+ onreset %Script; #IMPLIED -- the form was reset --
+ target %FrameTarget; #IMPLIED -- render in this frame --
+ accept-charset %Charsets; #IMPLIED -- list of supported charsets --
+ >
+
+<!-- Each label must not contain more than ONE field -->
+<!ELEMENT LABEL - - (%inline;)* -(LABEL) -- form field label text -->
+<!ATTLIST LABEL
+ %attrs; -- %coreattrs, %i18n, %events --
+ for IDREF #IMPLIED -- matches field ID value --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ >
+
+<!ENTITY % InputType
+ "(TEXT | PASSWORD | CHECKBOX |
+ RADIO | SUBMIT | RESET |
+ FILE | HIDDEN | IMAGE | BUTTON)"
+ >
+
+<!-- attribute name required for all but submit & reset -->
+<!ELEMENT INPUT - O EMPTY -- form control -->
+<!ATTLIST INPUT
+ %attrs; -- %coreattrs, %i18n, %events --
+ type %InputType; TEXT -- what kind of widget is needed --
+ name CDATA #IMPLIED -- submit as part of form --
+ value CDATA #IMPLIED -- required for radio and checkboxes --
+ checked (checked) #IMPLIED -- for radio buttons and check boxes --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ readonly (readonly) #IMPLIED -- for text and passwd --
+ size CDATA #IMPLIED -- specific to each type of field --
+ maxlength NUMBER #IMPLIED -- max chars for text fields --
+ src %URI; #IMPLIED -- for fields with images --
+ alt CDATA #IMPLIED -- short description --
+ usemap %URI; #IMPLIED -- use client-side image map --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onselect %Script; #IMPLIED -- some text was selected --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ accept %ContentTypes; #IMPLIED -- list of MIME types for file upload --
+ align %IAlign; #IMPLIED -- vertical or horizontal alignment --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT SELECT - - (OPTGROUP|OPTION)+ -- option selector -->
+<!ATTLIST SELECT
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED -- field name --
+ size NUMBER #IMPLIED -- rows visible --
+ multiple (multiple) #IMPLIED -- default is single selection --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!ELEMENT OPTGROUP - - (OPTION)+ -- option group -->
+<!ATTLIST OPTGROUP
+ %attrs; -- %coreattrs, %i18n, %events --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ label %Text; #REQUIRED -- for use in hierarchical menus --
+ >
+
+<!ELEMENT OPTION - O (#PCDATA) -- selectable choice -->
+<!ATTLIST OPTION
+ %attrs; -- %coreattrs, %i18n, %events --
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ label %Text; #IMPLIED -- for use in hierarchical menus --
+ value CDATA #IMPLIED -- defaults to element content --
+ >
+
+<!ELEMENT TEXTAREA - - (#PCDATA) -- multi-line text field -->
+<!ATTLIST TEXTAREA
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED
+ rows NUMBER #REQUIRED
+ cols NUMBER #REQUIRED
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ readonly (readonly) #IMPLIED
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ onselect %Script; #IMPLIED -- some text was selected --
+ onchange %Script; #IMPLIED -- the element value was changed --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!--
+ #PCDATA is to solve the mixed content problem,
+ per specification only whitespace is allowed there!
+ -->
+<!ELEMENT FIELDSET - - (#PCDATA,LEGEND,(%flow;)*) -- form control group -->
+<!ATTLIST FIELDSET
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!ELEMENT LEGEND - - (%inline;)* -- fieldset legend -->
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ATTLIST LEGEND
+ %attrs; -- %coreattrs, %i18n, %events --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ align %LAlign; #IMPLIED -- relative to fieldset --
+ >
+
+<!ELEMENT BUTTON - -
+ (%flow;)* -(A|%formctrl;|FORM|ISINDEX|FIELDSET|IFRAME)
+ -- push button -->
+<!ATTLIST BUTTON
+ %attrs; -- %coreattrs, %i18n, %events --
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED -- sent to server when submitted --
+ type (button|submit|reset) submit -- for use as form button --
+ disabled (disabled) #IMPLIED -- unavailable in this context --
+ tabindex NUMBER #IMPLIED -- position in tabbing order --
+ accesskey %Character; #IMPLIED -- accessibility key character --
+ onfocus %Script; #IMPLIED -- the element got the focus --
+ onblur %Script; #IMPLIED -- the element lost the focus --
+ %reserved; -- reserved for possible future use --
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The BORDER attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The FRAME attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the VALIGN attribute.
+
+ The value "border" is included for backwards compatibility with
+ <TABLE BORDER> which yields frame=border and border=implied
+ For <TABLE BORDER=1> you get border=1 and frame=implied. In this
+ case, it is appropriate to treat this as frame=border for backwards
+ compatibility with deployed browsers.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The RULES attribute defines which rules to draw between cells:
+
+ If RULES is absent then assume:
+ "none" if BORDER is absent or BORDER=0 otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents -->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED -- alignment char, e.g. char=':' --
+ charoff %Length; #IMPLIED -- offset for alignment char --"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT TABLE - -
+ (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)>
+<!ELEMENT CAPTION - - (%inline;)* -- table caption -->
+<!ELEMENT THEAD - O (TR)+ -- table header -->
+<!ELEMENT TFOOT - O (TR)+ -- table footer -->
+<!ELEMENT TBODY O O (TR)+ -- table body -->
+<!ELEMENT COLGROUP - O (col)* -- table column group -->
+<!ELEMENT COL - O EMPTY -- table column -->
+<!ELEMENT TR - O (TH|TD)+ -- table row -->
+<!ELEMENT (TH|TD) - O (%flow;)* -- table header cell, table data cell-->
+
+<!ATTLIST TABLE -- table element --
+ %attrs; -- %coreattrs, %i18n, %events --
+ summary %Text; #IMPLIED -- purpose/structure for speech output--
+ width %Length; #IMPLIED -- table width --
+ border %Pixels; #IMPLIED -- controls frame width around table --
+ frame %TFrame; #IMPLIED -- which parts of frame to render --
+ rules %TRules; #IMPLIED -- rulings between rows and cols --
+ cellspacing %Length; #IMPLIED -- spacing between cells --
+ cellpadding %Length; #IMPLIED -- spacing within cells --
+ align %TAlign; #IMPLIED -- table position relative to window --
+ bgcolor %Color; #IMPLIED -- background color for cells --
+ %reserved; -- reserved for possible future use --
+ datapagesize CDATA #IMPLIED -- reserved for possible future use --
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST CAPTION
+ %attrs; -- %coreattrs, %i18n, %events --
+ align %CAlign; #IMPLIED -- relative to table --
+ >
+
+<!--
+COLGROUP groups a set of COL elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST COLGROUP
+ %attrs; -- %coreattrs, %i18n, %events --
+ span NUMBER 1 -- default number of columns in group --
+ width %MultiLength; #IMPLIED -- default width for enclosed COLs --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!--
+ COL elements define the alignment properties for cells in
+ one or more columns.
+
+ The WIDTH attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The SPAN attribute causes the attributes of one
+ COL element to apply to more than one column.
+-->
+<!ATTLIST COL -- column groups and properties --
+ %attrs; -- %coreattrs, %i18n, %events --
+ span NUMBER 1 -- COL attributes affect N columns --
+ width %MultiLength; #IMPLIED -- column width specification --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!--
+ Use THEAD to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ TBODY sections are rendered in scrolling panel.
+
+ Use TFOOT to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ TBODY sections are rendered in scrolling panel.
+
+ Use multiple TBODY sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST (THEAD|TBODY|TFOOT) -- table section --
+ %attrs; -- %coreattrs, %i18n, %events --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ >
+
+<!ATTLIST TR -- table row --
+ %attrs; -- %coreattrs, %i18n, %events --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ bgcolor %Color; #IMPLIED -- background color for row --
+ >
+
+
+<!-- Scope is simpler than axes attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- TH is for headers, TD for data, but for cells acting as both use TD -->
+<!ATTLIST (TH|TD) -- header or data cell --
+ %attrs; -- %coreattrs, %i18n, %events --
+ abbr %Text; #IMPLIED -- abbreviation for header cell --
+ axis CDATA #IMPLIED -- names groups of related headers--
+ headers IDREFS #IMPLIED -- list of id's for header cells --
+ scope %Scope; #IMPLIED -- scope covered by header cells --
+ rowspan NUMBER 1 -- number of rows spanned by cell --
+ colspan NUMBER 1 -- number of cols spanned by cell --
+ %cellhalign; -- horizontal alignment in cells --
+ %cellvalign; -- vertical alignment in cells --
+ nowrap (nowrap) #IMPLIED -- suppress word wrap --
+ bgcolor %Color; #IMPLIED -- cell background color --
+ width %Pixels; #IMPLIED -- width for cell --
+ height %Pixels; #IMPLIED -- height for cell --
+ >
+
+<!--================== Document Frames ===================================-->
+
+<!--
+ The content model for HTML documents depends on whether the HEAD is
+ followed by a FRAMESET or BODY element. The widespread omission of
+ the BODY start tag makes it impractical to define the content model
+ without the use of a marked section.
+-->
+
+<!-- Feature Switch for frameset documents -->
+<!ENTITY % HTML.Frameset "IGNORE">
+
+<![ %HTML.Frameset; [
+<!ELEMENT FRAMESET - - ((FRAMESET|FRAME)+ & NOFRAMES?) -- window subdivision-->
+<!ATTLIST FRAMESET
+ %coreattrs; -- id, class, style, title --
+ rows %MultiLengths; #IMPLIED -- list of lengths,
+ default: 100% (1 row) --
+ cols %MultiLengths; #IMPLIED -- list of lengths,
+ default: 100% (1 col) --
+ onload %Script; #IMPLIED -- all the frames have been loaded --
+ onunload %Script; #IMPLIED -- all the frames have been removed --
+ >
+]]>
+
+<![ %HTML.Frameset; [
+<!-- reserved frame names start with "_" otherwise starts with letter -->
+<!ELEMENT FRAME - O EMPTY -- subwindow -->
+<!ATTLIST FRAME
+ %coreattrs; -- id, class, style, title --
+ longdesc %URI; #IMPLIED -- link to long description
+ (complements title) --
+ name CDATA #IMPLIED -- name of frame for targetting --
+ src %URI; #IMPLIED -- source of frame content --
+ frameborder (1|0) 1 -- request frame borders? --
+ marginwidth %Pixels; #IMPLIED -- margin widths in pixels --
+ marginheight %Pixels; #IMPLIED -- margin height in pixels --
+ noresize (noresize) #IMPLIED -- allow users to resize frames? --
+ scrolling (yes|no|auto) auto -- scrollbar or none --
+ >
+]]>
+
+<!ELEMENT IFRAME - - (%flow;)* -- inline subwindow -->
+<!ATTLIST IFRAME
+ %coreattrs; -- id, class, style, title --
+ longdesc %URI; #IMPLIED -- link to long description
+ (complements title) --
+ name CDATA #IMPLIED -- name of frame for targetting --
+ src %URI; #IMPLIED -- source of frame content --
+ frameborder (1|0) 1 -- request frame borders? --
+ marginwidth %Pixels; #IMPLIED -- margin widths in pixels --
+ marginheight %Pixels; #IMPLIED -- margin height in pixels --
+ scrolling (yes|no|auto) auto -- scrollbar or none --
+ align %IAlign; #IMPLIED -- vertical or horizontal alignment --
+ height %Length; #IMPLIED -- frame height --
+ width %Length; #IMPLIED -- frame width --
+ >
+
+<![ %HTML.Frameset; [
+<!ENTITY % noframes.content "(BODY) -(NOFRAMES)">
+]]>
+
+<!ENTITY % noframes.content "(%flow;)*">
+
+<!ELEMENT NOFRAMES - - %noframes.content;
+ -- alternate content container for non frame-based rendering -->
+<!ATTLIST NOFRAMES
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--================ Document Head =======================================-->
+<!-- %head.misc; defined earlier on as "SCRIPT|STYLE|META|LINK|OBJECT" -->
+<!ENTITY % head.content "TITLE & ISINDEX? & BASE?">
+
+<!ELEMENT HEAD O O (%head.content;) +(%head.misc;) -- document head -->
+<!ATTLIST HEAD
+ %i18n; -- lang, dir --
+ profile %URI; #IMPLIED -- named dictionary of meta info --
+ >
+
+<!-- The TITLE element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT TITLE - - (#PCDATA) -(%head.misc;) -- document title -->
+<!ATTLIST TITLE %i18n>
+
+<!ELEMENT ISINDEX - O EMPTY -- single line prompt -->
+<!ATTLIST ISINDEX
+ %coreattrs; -- id, class, style, title --
+ %i18n; -- lang, dir --
+ prompt %Text; #IMPLIED -- prompt message -->
+
+<!ELEMENT BASE - O EMPTY -- document base URI -->
+<!ATTLIST BASE
+ href %URI; #IMPLIED -- URI that acts as base URI --
+ target %FrameTarget; #IMPLIED -- render in this frame --
+ >
+
+<!ELEMENT META - O EMPTY -- generic metainformation -->
+<!ATTLIST META
+ %i18n; -- lang, dir, for use with content --
+ http-equiv NAME #IMPLIED -- HTTP response header name --
+ name NAME #IMPLIED -- metainformation name --
+ content CDATA #REQUIRED -- associated information --
+ scheme CDATA #IMPLIED -- select form of content --
+ >
+
+<!ELEMENT STYLE - - %StyleSheet -- style info -->
+<!ATTLIST STYLE
+ %i18n; -- lang, dir, for use with title --
+ type %ContentType; #REQUIRED -- content type of style language --
+ media %MediaDesc; #IMPLIED -- designed for use with these media --
+ title %Text; #IMPLIED -- advisory title --
+ >
+
+<!ELEMENT SCRIPT - - %Script; -- script statements -->
+<!ATTLIST SCRIPT
+ charset %Charset; #IMPLIED -- char encoding of linked resource --
+ type %ContentType; #REQUIRED -- content type of script language --
+ language CDATA #IMPLIED -- predefined script language name --
+ src %URI; #IMPLIED -- URI for an external script --
+ defer (defer) #IMPLIED -- UA may defer execution of script --
+ event CDATA #IMPLIED -- reserved for possible future use --
+ for %URI; #IMPLIED -- reserved for possible future use --
+ >
+
+<!ELEMENT NOSCRIPT - - (%flow;)*
+ -- alternate content container for non script-based rendering -->
+<!ATTLIST NOSCRIPT
+ %attrs; -- %coreattrs, %i18n, %events --
+ >
+
+<!--================ Document Structure ==================================-->
+<!ENTITY % version "version CDATA #FIXED '%HTML.Version;'">
+
+<![ %HTML.Frameset; [
+<!ENTITY % html.content "HEAD, FRAMESET">
+]]>
+
+<!ENTITY % html.content "HEAD, BODY">
+
+<!ELEMENT HTML O O (%html.content;) -- document root element -->
+<!ATTLIST HTML
+ %i18n; -- lang, dir --
+ %version;
+ >
diff --git a/tests/dtds/HTML4.soc b/tests/dtds/HTML4.soc
new file mode 100644
index 0000000..ec4825f
--- /dev/null
+++ b/tests/dtds/HTML4.soc
@@ -0,0 +1,9 @@
+OVERRIDE YES
+SGMLDECL HTML4.dcl
+DOCTYPE HTML HTML4.dtd
+PUBLIC "-//W3C//DTD HTML 4.0//EN" HTML4-s.dtd
+PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" HTML4.dtd
+PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" HTML4-f.dtd
+PUBLIC "-//W3C//ENTITIES Latin1//EN//HTML" HTMLlat1.ent
+PUBLIC "-//W3C//ENTITIES Special//EN//HTML" HTMLspec.ent
+PUBLIC "-//W3C//ENTITIES Symbols//EN//HTML" HTMLsym.ent
diff --git a/tests/dtds/HTMLlat1.ent b/tests/dtds/HTMLlat1.ent
new file mode 100644
index 0000000..7632023
--- /dev/null
+++ b/tests/dtds/HTMLlat1.ent
@@ -0,0 +1,195 @@
+<!-- Portions (C) International Organization for Standardization 1986
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Full Latin 1//EN//HTML">
+ %HTMLlat1;
+-->
+
+<!ENTITY nbsp CDATA "&#160;" -- no-break space = non-breaking space,
+ U+00A0 ISOnum -->
+<!ENTITY iexcl CDATA "&#161;" -- inverted exclamation mark, U+00A1 ISOnum -->
+<!ENTITY cent CDATA "&#162;" -- cent sign, U+00A2 ISOnum -->
+<!ENTITY pound CDATA "&#163;" -- pound sign, U+00A3 ISOnum -->
+<!ENTITY curren CDATA "&#164;" -- currency sign, U+00A4 ISOnum -->
+<!ENTITY yen CDATA "&#165;" -- yen sign = yuan sign, U+00A5 ISOnum -->
+<!ENTITY brvbar CDATA "&#166;" -- broken bar = broken vertical bar,
+ U+00A6 ISOnum -->
+<!ENTITY sect CDATA "&#167;" -- section sign, U+00A7 ISOnum -->
+<!ENTITY uml CDATA "&#168;" -- diaeresis = spacing diaeresis,
+ U+00A8 ISOdia -->
+<!ENTITY copy CDATA "&#169;" -- copyright sign, U+00A9 ISOnum -->
+<!ENTITY ordf CDATA "&#170;" -- feminine ordinal indicator, U+00AA ISOnum -->
+<!ENTITY laquo CDATA "&#171;" -- left-pointing double angle quotation mark
+ = left pointing guillemet, U+00AB ISOnum -->
+<!ENTITY not CDATA "&#172;" -- not sign = discretionary hyphen,
+ U+00AC ISOnum -->
+<!ENTITY shy CDATA "&#173;" -- soft hyphen = discretionary hyphen,
+ U+00AD ISOnum -->
+<!ENTITY reg CDATA "&#174;" -- registered sign = registered trade mark sign,
+ U+00AE ISOnum -->
+<!ENTITY macr CDATA "&#175;" -- macron = spacing macron = overline
+ = APL overbar, U+00AF ISOdia -->
+<!ENTITY deg CDATA "&#176;" -- degree sign, U+00B0 ISOnum -->
+<!ENTITY plusmn CDATA "&#177;" -- plus-minus sign = plus-or-minus sign,
+ U+00B1 ISOnum -->
+<!ENTITY sup2 CDATA "&#178;" -- superscript two = superscript digit two
+ = squared, U+00B2 ISOnum -->
+<!ENTITY sup3 CDATA "&#179;" -- superscript three = superscript digit three
+ = cubed, U+00B3 ISOnum -->
+<!ENTITY acute CDATA "&#180;" -- acute accent = spacing acute,
+ U+00B4 ISOdia -->
+<!ENTITY micro CDATA "&#181;" -- micro sign, U+00B5 ISOnum -->
+<!ENTITY para CDATA "&#182;" -- pilcrow sign = paragraph sign,
+ U+00B6 ISOnum -->
+<!ENTITY middot CDATA "&#183;" -- middle dot = Georgian comma
+ = Greek middle dot, U+00B7 ISOnum -->
+<!ENTITY cedil CDATA "&#184;" -- cedilla = spacing cedilla, U+00B8 ISOdia -->
+<!ENTITY sup1 CDATA "&#185;" -- superscript one = superscript digit one,
+ U+00B9 ISOnum -->
+<!ENTITY ordm CDATA "&#186;" -- masculine ordinal indicator,
+ U+00BA ISOnum -->
+<!ENTITY raquo CDATA "&#187;" -- right-pointing double angle quotation mark
+ = right pointing guillemet, U+00BB ISOnum -->
+<!ENTITY frac14 CDATA "&#188;" -- vulgar fraction one quarter
+ = fraction one quarter, U+00BC ISOnum -->
+<!ENTITY frac12 CDATA "&#189;" -- vulgar fraction one half
+ = fraction one half, U+00BD ISOnum -->
+<!ENTITY frac34 CDATA "&#190;" -- vulgar fraction three quarters
+ = fraction three quarters, U+00BE ISOnum -->
+<!ENTITY iquest CDATA "&#191;" -- inverted question mark
+ = turned question mark, U+00BF ISOnum -->
+<!ENTITY Agrave CDATA "&#192;" -- latin capital letter A with grave
+ = latin capital letter A grave,
+ U+00C0 ISOlat1 -->
+<!ENTITY Aacute CDATA "&#193;" -- latin capital letter A with acute,
+ U+00C1 ISOlat1 -->
+<!ENTITY Acirc CDATA "&#194;" -- latin capital letter A with circumflex,
+ U+00C2 ISOlat1 -->
+<!ENTITY Atilde CDATA "&#195;" -- latin capital letter A with tilde,
+ U+00C3 ISOlat1 -->
+<!ENTITY Auml CDATA "&#196;" -- latin capital letter A with diaeresis,
+ U+00C4 ISOlat1 -->
+<!ENTITY Aring CDATA "&#197;" -- latin capital letter A with ring above
+ = latin capital letter A ring,
+ U+00C5 ISOlat1 -->
+<!ENTITY AElig CDATA "&#198;" -- latin capital letter AE
+ = latin capital ligature AE,
+ U+00C6 ISOlat1 -->
+<!ENTITY Ccedil CDATA "&#199;" -- latin capital letter C with cedilla,
+ U+00C7 ISOlat1 -->
+<!ENTITY Egrave CDATA "&#200;" -- latin capital letter E with grave,
+ U+00C8 ISOlat1 -->
+<!ENTITY Eacute CDATA "&#201;" -- latin capital letter E with acute,
+ U+00C9 ISOlat1 -->
+<!ENTITY Ecirc CDATA "&#202;" -- latin capital letter E with circumflex,
+ U+00CA ISOlat1 -->
+<!ENTITY Euml CDATA "&#203;" -- latin capital letter E with diaeresis,
+ U+00CB ISOlat1 -->
+<!ENTITY Igrave CDATA "&#204;" -- latin capital letter I with grave,
+ U+00CC ISOlat1 -->
+<!ENTITY Iacute CDATA "&#205;" -- latin capital letter I with acute,
+ U+00CD ISOlat1 -->
+<!ENTITY Icirc CDATA "&#206;" -- latin capital letter I with circumflex,
+ U+00CE ISOlat1 -->
+<!ENTITY Iuml CDATA "&#207;" -- latin capital letter I with diaeresis,
+ U+00CF ISOlat1 -->
+<!ENTITY ETH CDATA "&#208;" -- latin capital letter ETH, U+00D0 ISOlat1 -->
+<!ENTITY Ntilde CDATA "&#209;" -- latin capital letter N with tilde,
+ U+00D1 ISOlat1 -->
+<!ENTITY Ograve CDATA "&#210;" -- latin capital letter O with grave,
+ U+00D2 ISOlat1 -->
+<!ENTITY Oacute CDATA "&#211;" -- latin capital letter O with acute,
+ U+00D3 ISOlat1 -->
+<!ENTITY Ocirc CDATA "&#212;" -- latin capital letter O with circumflex,
+ U+00D4 ISOlat1 -->
+<!ENTITY Otilde CDATA "&#213;" -- latin capital letter O with tilde,
+ U+00D5 ISOlat1 -->
+<!ENTITY Ouml CDATA "&#214;" -- latin capital letter O with diaeresis,
+ U+00D6 ISOlat1 -->
+<!ENTITY times CDATA "&#215;" -- multiplication sign, U+00D7 ISOnum -->
+<!ENTITY Oslash CDATA "&#216;" -- latin capital letter O with stroke
+ = latin capital letter O slash,
+ U+00D8 ISOlat1 -->
+<!ENTITY Ugrave CDATA "&#217;" -- latin capital letter U with grave,
+ U+00D9 ISOlat1 -->
+<!ENTITY Uacute CDATA "&#218;" -- latin capital letter U with acute,
+ U+00DA ISOlat1 -->
+<!ENTITY Ucirc CDATA "&#219;" -- latin capital letter U with circumflex,
+ U+00DB ISOlat1 -->
+<!ENTITY Uuml CDATA "&#220;" -- latin capital letter U with diaeresis,
+ U+00DC ISOlat1 -->
+<!ENTITY Yacute CDATA "&#221;" -- latin capital letter Y with acute,
+ U+00DD ISOlat1 -->
+<!ENTITY THORN CDATA "&#222;" -- latin capital letter THORN,
+ U+00DE ISOlat1 -->
+<!ENTITY szlig CDATA "&#223;" -- latin small letter sharp s = ess-zed,
+ U+00DF ISOlat1 -->
+<!ENTITY agrave CDATA "&#224;" -- latin small letter a with grave
+ = latin small letter a grave,
+ U+00E0 ISOlat1 -->
+<!ENTITY aacute CDATA "&#225;" -- latin small letter a with acute,
+ U+00E1 ISOlat1 -->
+<!ENTITY acirc CDATA "&#226;" -- latin small letter a with circumflex,
+ U+00E2 ISOlat1 -->
+<!ENTITY atilde CDATA "&#227;" -- latin small letter a with tilde,
+ U+00E3 ISOlat1 -->
+<!ENTITY auml CDATA "&#228;" -- latin small letter a with diaeresis,
+ U+00E4 ISOlat1 -->
+<!ENTITY aring CDATA "&#229;" -- latin small letter a with ring above
+ = latin small letter a ring,
+ U+00E5 ISOlat1 -->
+<!ENTITY aelig CDATA "&#230;" -- latin small letter ae
+ = latin small ligature ae, U+00E6 ISOlat1 -->
+<!ENTITY ccedil CDATA "&#231;" -- latin small letter c with cedilla,
+ U+00E7 ISOlat1 -->
+<!ENTITY egrave CDATA "&#232;" -- latin small letter e with grave,
+ U+00E8 ISOlat1 -->
+<!ENTITY eacute CDATA "&#233;" -- latin small letter e with acute,
+ U+00E9 ISOlat1 -->
+<!ENTITY ecirc CDATA "&#234;" -- latin small letter e with circumflex,
+ U+00EA ISOlat1 -->
+<!ENTITY euml CDATA "&#235;" -- latin small letter e with diaeresis,
+ U+00EB ISOlat1 -->
+<!ENTITY igrave CDATA "&#236;" -- latin small letter i with grave,
+ U+00EC ISOlat1 -->
+<!ENTITY iacute CDATA "&#237;" -- latin small letter i with acute,
+ U+00ED ISOlat1 -->
+<!ENTITY icirc CDATA "&#238;" -- latin small letter i with circumflex,
+ U+00EE ISOlat1 -->
+<!ENTITY iuml CDATA "&#239;" -- latin small letter i with diaeresis,
+ U+00EF ISOlat1 -->
+<!ENTITY eth CDATA "&#240;" -- latin small letter eth, U+00F0 ISOlat1 -->
+<!ENTITY ntilde CDATA "&#241;" -- latin small letter n with tilde,
+ U+00F1 ISOlat1 -->
+<!ENTITY ograve CDATA "&#242;" -- latin small letter o with grave,
+ U+00F2 ISOlat1 -->
+<!ENTITY oacute CDATA "&#243;" -- latin small letter o with acute,
+ U+00F3 ISOlat1 -->
+<!ENTITY ocirc CDATA "&#244;" -- latin small letter o with circumflex,
+ U+00F4 ISOlat1 -->
+<!ENTITY otilde CDATA "&#245;" -- latin small letter o with tilde,
+ U+00F5 ISOlat1 -->
+<!ENTITY ouml CDATA "&#246;" -- latin small letter o with diaeresis,
+ U+00F6 ISOlat1 -->
+<!ENTITY divide CDATA "&#247;" -- division sign, U+00F7 ISOnum -->
+<!ENTITY oslash CDATA "&#248;" -- latin small letter o with stroke,
+ = latin small letter o slash,
+ U+00F8 ISOlat1 -->
+<!ENTITY ugrave CDATA "&#249;" -- latin small letter u with grave,
+ U+00F9 ISOlat1 -->
+<!ENTITY uacute CDATA "&#250;" -- latin small letter u with acute,
+ U+00FA ISOlat1 -->
+<!ENTITY ucirc CDATA "&#251;" -- latin small letter u with circumflex,
+ U+00FB ISOlat1 -->
+<!ENTITY uuml CDATA "&#252;" -- latin small letter u with diaeresis,
+ U+00FC ISOlat1 -->
+<!ENTITY yacute CDATA "&#253;" -- latin small letter y with acute,
+ U+00FD ISOlat1 -->
+<!ENTITY thorn CDATA "&#254;" -- latin small letter thorn with,
+ U+00FE ISOlat1 -->
+<!ENTITY yuml CDATA "&#255;" -- latin small letter y with diaeresis,
+ U+00FF ISOlat1 --> \ No newline at end of file
diff --git a/tests/dtds/HTMLspec.ent b/tests/dtds/HTMLspec.ent
new file mode 100644
index 0000000..29011cc
--- /dev/null
+++ b/tests/dtds/HTMLspec.ent
@@ -0,0 +1,77 @@
+<!-- Special characters for HTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special//EN//HTML">
+ %HTMLspecial; -->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. CDATA values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode 2.0 names.
+
+-->
+
+<!-- C0 Controls and Basic Latin -->
+<!ENTITY quot CDATA "&#34;" -- quotation mark = APL quote,
+ U+0022 ISOnum -->
+<!ENTITY amp CDATA "&#38;" -- ampersand, U+0026 ISOnum -->
+<!ENTITY lt CDATA "&#60;" -- less-than sign, U+003C ISOnum -->
+<!ENTITY gt CDATA "&#62;" -- greater-than sign, U+003E ISOnum -->
+
+<!-- Latin Extended-A -->
+<!ENTITY OElig CDATA "&#338;" -- latin capital ligature OE,
+ U+0152 ISOlat2 -->
+<!ENTITY oelig CDATA "&#339;" -- latin small ligature oe, U+0153 ISOlat2 -->
+<!-- ligature is a misnomer, this is a separate character in some languages -->
+<!ENTITY Scaron CDATA "&#352;" -- latin capital letter S with caron,
+ U+0160 ISOlat2 -->
+<!ENTITY scaron CDATA "&#353;" -- latin small letter s with caron,
+ U+0161 ISOlat2 -->
+<!ENTITY Yuml CDATA "&#376;" -- latin capital letter Y with diaeresis,
+ U+0178 ISOlat2 -->
+
+<!-- Spacing Modifier Letters -->
+<!ENTITY circ CDATA "&#710;" -- modifier letter circumflex accent,
+ U+02C6 ISOpub -->
+<!ENTITY tilde CDATA "&#732;" -- small tilde, U+02DC ISOdia -->
+
+<!-- General Punctuation -->
+<!ENTITY ensp CDATA "&#8194;" -- en space, U+2002 ISOpub -->
+<!ENTITY emsp CDATA "&#8195;" -- em space, U+2003 ISOpub -->
+<!ENTITY thinsp CDATA "&#8201;" -- thin space, U+2009 ISOpub -->
+<!ENTITY zwnj CDATA "&#8204;" -- zero width non-joiner,
+ U+200C NEW RFC 2070 -->
+<!ENTITY zwj CDATA "&#8205;" -- zero width joiner, U+200D NEW RFC 2070 -->
+<!ENTITY lrm CDATA "&#8206;" -- left-to-right mark, U+200E NEW RFC 2070 -->
+<!ENTITY rlm CDATA "&#8207;" -- right-to-left mark, U+200F NEW RFC 2070 -->
+<!ENTITY ndash CDATA "&#8211;" -- en dash, U+2013 ISOpub -->
+<!ENTITY mdash CDATA "&#8212;" -- em dash, U+2014 ISOpub -->
+<!ENTITY lsquo CDATA "&#8216;" -- left single quotation mark,
+ U+2018 ISOnum -->
+<!ENTITY rsquo CDATA "&#8217;" -- right single quotation mark,
+ U+2019 ISOnum -->
+<!ENTITY sbquo CDATA "&#8218;" -- single low-9 quotation mark, U+201A NEW -->
+<!ENTITY ldquo CDATA "&#8220;" -- left double quotation mark,
+ U+201C ISOnum -->
+<!ENTITY rdquo CDATA "&#8221;" -- right double quotation mark,
+ U+201D ISOnum -->
+<!ENTITY bdquo CDATA "&#8222;" -- double low-9 quotation mark, U+201E NEW -->
+<!ENTITY dagger CDATA "&#8224;" -- dagger, U+2020 ISOpub -->
+<!ENTITY Dagger CDATA "&#8225;" -- double dagger, U+2021 ISOpub -->
+<!ENTITY permil CDATA "&#8240;" -- per mille sign, U+2030 ISOtech -->
+<!ENTITY lsaquo CDATA "&#8249;" -- single left-pointing angle quotation mark,
+ U+2039 ISO proposed -->
+<!-- lsaquo is proposed but not yet ISO standardized -->
+<!ENTITY rsaquo CDATA "&#8250;" -- single right-pointing angle quotation mark,
+ U+203A ISO proposed -->
+<!-- rsaquo is proposed but not yet ISO standardized -->
+<!ENTITY euro CDATA "&#8364;" -- euro sign, U+20AC NEW --> \ No newline at end of file
diff --git a/tests/dtds/HTMLsym.ent b/tests/dtds/HTMLsym.ent
new file mode 100644
index 0000000..2a6250b
--- /dev/null
+++ b/tests/dtds/HTMLsym.ent
@@ -0,0 +1,241 @@
+<!-- Mathematical, Greek and Symbolic characters for HTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbolic//EN//HTML">
+ %HTMLsymbol; -->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. CDATA values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode 2.0 names.
+
+-->
+
+<!-- Latin Extended-B -->
+<!ENTITY fnof CDATA "&#402;" -- latin small f with hook = function
+ = florin, U+0192 ISOtech -->
+
+<!-- Greek -->
+<!ENTITY Alpha CDATA "&#913;" -- greek capital letter alpha, U+0391 -->
+<!ENTITY Beta CDATA "&#914;" -- greek capital letter beta, U+0392 -->
+<!ENTITY Gamma CDATA "&#915;" -- greek capital letter gamma,
+ U+0393 ISOgrk3 -->
+<!ENTITY Delta CDATA "&#916;" -- greek capital letter delta,
+ U+0394 ISOgrk3 -->
+<!ENTITY Epsilon CDATA "&#917;" -- greek capital letter epsilon, U+0395 -->
+<!ENTITY Zeta CDATA "&#918;" -- greek capital letter zeta, U+0396 -->
+<!ENTITY Eta CDATA "&#919;" -- greek capital letter eta, U+0397 -->
+<!ENTITY Theta CDATA "&#920;" -- greek capital letter theta,
+ U+0398 ISOgrk3 -->
+<!ENTITY Iota CDATA "&#921;" -- greek capital letter iota, U+0399 -->
+<!ENTITY Kappa CDATA "&#922;" -- greek capital letter kappa, U+039A -->
+<!ENTITY Lambda CDATA "&#923;" -- greek capital letter lambda,
+ U+039B ISOgrk3 -->
+<!ENTITY Mu CDATA "&#924;" -- greek capital letter mu, U+039C -->
+<!ENTITY Nu CDATA "&#925;" -- greek capital letter nu, U+039D -->
+<!ENTITY Xi CDATA "&#926;" -- greek capital letter xi, U+039E ISOgrk3 -->
+<!ENTITY Omicron CDATA "&#927;" -- greek capital letter omicron, U+039F -->
+<!ENTITY Pi CDATA "&#928;" -- greek capital letter pi, U+03A0 ISOgrk3 -->
+<!ENTITY Rho CDATA "&#929;" -- greek capital letter rho, U+03A1 -->
+<!-- there is no Sigmaf, and no U+03A2 character either -->
+<!ENTITY Sigma CDATA "&#931;" -- greek capital letter sigma,
+ U+03A3 ISOgrk3 -->
+<!ENTITY Tau CDATA "&#932;" -- greek capital letter tau, U+03A4 -->
+<!ENTITY Upsilon CDATA "&#933;" -- greek capital letter upsilon,
+ U+03A5 ISOgrk3 -->
+<!ENTITY Phi CDATA "&#934;" -- greek capital letter phi,
+ U+03A6 ISOgrk3 -->
+<!ENTITY Chi CDATA "&#935;" -- greek capital letter chi, U+03A7 -->
+<!ENTITY Psi CDATA "&#936;" -- greek capital letter psi,
+ U+03A8 ISOgrk3 -->
+<!ENTITY Omega CDATA "&#937;" -- greek capital letter omega,
+ U+03A9 ISOgrk3 -->
+
+<!ENTITY alpha CDATA "&#945;" -- greek small letter alpha,
+ U+03B1 ISOgrk3 -->
+<!ENTITY beta CDATA "&#946;" -- greek small letter beta, U+03B2 ISOgrk3 -->
+<!ENTITY gamma CDATA "&#947;" -- greek small letter gamma,
+ U+03B3 ISOgrk3 -->
+<!ENTITY delta CDATA "&#948;" -- greek small letter delta,
+ U+03B4 ISOgrk3 -->
+<!ENTITY epsilon CDATA "&#949;" -- greek small letter epsilon,
+ U+03B5 ISOgrk3 -->
+<!ENTITY zeta CDATA "&#950;" -- greek small letter zeta, U+03B6 ISOgrk3 -->
+<!ENTITY eta CDATA "&#951;" -- greek small letter eta, U+03B7 ISOgrk3 -->
+<!ENTITY theta CDATA "&#952;" -- greek small letter theta,
+ U+03B8 ISOgrk3 -->
+<!ENTITY iota CDATA "&#953;" -- greek small letter iota, U+03B9 ISOgrk3 -->
+<!ENTITY kappa CDATA "&#954;" -- greek small letter kappa,
+ U+03BA ISOgrk3 -->
+<!ENTITY lambda CDATA "&#955;" -- greek small letter lambda,
+ U+03BB ISOgrk3 -->
+<!ENTITY mu CDATA "&#956;" -- greek small letter mu, U+03BC ISOgrk3 -->
+<!ENTITY nu CDATA "&#957;" -- greek small letter nu, U+03BD ISOgrk3 -->
+<!ENTITY xi CDATA "&#958;" -- greek small letter xi, U+03BE ISOgrk3 -->
+<!ENTITY omicron CDATA "&#959;" -- greek small letter omicron, U+03BF NEW -->
+<!ENTITY pi CDATA "&#960;" -- greek small letter pi, U+03C0 ISOgrk3 -->
+<!ENTITY rho CDATA "&#961;" -- greek small letter rho, U+03C1 ISOgrk3 -->
+<!ENTITY sigmaf CDATA "&#962;" -- greek small letter final sigma,
+ U+03C2 ISOgrk3 -->
+<!ENTITY sigma CDATA "&#963;" -- greek small letter sigma,
+ U+03C3 ISOgrk3 -->
+<!ENTITY tau CDATA "&#964;" -- greek small letter tau, U+03C4 ISOgrk3 -->
+<!ENTITY upsilon CDATA "&#965;" -- greek small letter upsilon,
+ U+03C5 ISOgrk3 -->
+<!ENTITY phi CDATA "&#966;" -- greek small letter phi, U+03C6 ISOgrk3 -->
+<!ENTITY chi CDATA "&#967;" -- greek small letter chi, U+03C7 ISOgrk3 -->
+<!ENTITY psi CDATA "&#968;" -- greek small letter psi, U+03C8 ISOgrk3 -->
+<!ENTITY omega CDATA "&#969;" -- greek small letter omega,
+ U+03C9 ISOgrk3 -->
+<!ENTITY thetasym CDATA "&#977;" -- greek small letter theta symbol,
+ U+03D1 NEW -->
+<!ENTITY upsih CDATA "&#978;" -- greek upsilon with hook symbol,
+ U+03D2 NEW -->
+<!ENTITY piv CDATA "&#982;" -- greek pi symbol, U+03D6 ISOgrk3 -->
+
+<!-- General Punctuation -->
+<!ENTITY bull CDATA "&#8226;" -- bullet = black small circle,
+ U+2022 ISOpub -->
+<!-- bullet is NOT the same as bullet operator, U+2219 -->
+<!ENTITY hellip CDATA "&#8230;" -- horizontal ellipsis = three dot leader,
+ U+2026 ISOpub -->
+<!ENTITY prime CDATA "&#8242;" -- prime = minutes = feet, U+2032 ISOtech -->
+<!ENTITY Prime CDATA "&#8243;" -- double prime = seconds = inches,
+ U+2033 ISOtech -->
+<!ENTITY oline CDATA "&#8254;" -- overline = spacing overscore,
+ U+203E NEW -->
+<!ENTITY frasl CDATA "&#8260;" -- fraction slash, U+2044 NEW -->
+
+<!-- Letterlike Symbols -->
+<!ENTITY weierp CDATA "&#8472;" -- script capital P = power set
+ = Weierstrass p, U+2118 ISOamso -->
+<!ENTITY image CDATA "&#8465;" -- blackletter capital I = imaginary part,
+ U+2111 ISOamso -->
+<!ENTITY real CDATA "&#8476;" -- blackletter capital R = real part symbol,
+ U+211C ISOamso -->
+<!ENTITY trade CDATA "&#8482;" -- trade mark sign, U+2122 ISOnum -->
+<!ENTITY alefsym CDATA "&#8501;" -- alef symbol = first transfinite cardinal,
+ U+2135 NEW -->
+<!-- alef symbol is NOT the same as hebrew letter alef,
+ U+05D0 although the same glyph could be used to depict both characters -->
+
+<!-- Arrows -->
+<!ENTITY larr CDATA "&#8592;" -- leftwards arrow, U+2190 ISOnum -->
+<!ENTITY uarr CDATA "&#8593;" -- upwards arrow, U+2191 ISOnum-->
+<!ENTITY rarr CDATA "&#8594;" -- rightwards arrow, U+2192 ISOnum -->
+<!ENTITY darr CDATA "&#8595;" -- downwards arrow, U+2193 ISOnum -->
+<!ENTITY harr CDATA "&#8596;" -- left right arrow, U+2194 ISOamsa -->
+<!ENTITY crarr CDATA "&#8629;" -- downwards arrow with corner leftwards
+ = carriage return, U+21B5 NEW -->
+<!ENTITY lArr CDATA "&#8656;" -- leftwards double arrow, U+21D0 ISOtech -->
+<!-- Unicode does not say that lArr is the same as the 'is implied by' arrow
+ but also does not have any other character for that function. So ? lArr can
+ be used for 'is implied by' as ISOtech suggests -->
+<!ENTITY uArr CDATA "&#8657;" -- upwards double arrow, U+21D1 ISOamsa -->
+<!ENTITY rArr CDATA "&#8658;" -- rightwards double arrow,
+ U+21D2 ISOtech -->
+<!-- Unicode does not say this is the 'implies' character but does not have
+ another character with this function so ?
+ rArr can be used for 'implies' as ISOtech suggests -->
+<!ENTITY dArr CDATA "&#8659;" -- downwards double arrow, U+21D3 ISOamsa -->
+<!ENTITY hArr CDATA "&#8660;" -- left right double arrow,
+ U+21D4 ISOamsa -->
+
+<!-- Mathematical Operators -->
+<!ENTITY forall CDATA "&#8704;" -- for all, U+2200 ISOtech -->
+<!ENTITY part CDATA "&#8706;" -- partial differential, U+2202 ISOtech -->
+<!ENTITY exist CDATA "&#8707;" -- there exists, U+2203 ISOtech -->
+<!ENTITY empty CDATA "&#8709;" -- empty set = null set = diameter,
+ U+2205 ISOamso -->
+<!ENTITY nabla CDATA "&#8711;" -- nabla = backward difference,
+ U+2207 ISOtech -->
+<!ENTITY isin CDATA "&#8712;" -- element of, U+2208 ISOtech -->
+<!ENTITY notin CDATA "&#8713;" -- not an element of, U+2209 ISOtech -->
+<!ENTITY ni CDATA "&#8715;" -- contains as member, U+220B ISOtech -->
+<!-- should there be a more memorable name than 'ni'? -->
+<!ENTITY prod CDATA "&#8719;" -- n-ary product = product sign,
+ U+220F ISOamsb -->
+<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+ the same glyph might be used for both -->
+<!ENTITY sum CDATA "&#8721;" -- n-ary sumation, U+2211 ISOamsb -->
+<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+ though the same glyph might be used for both -->
+<!ENTITY minus CDATA "&#8722;" -- minus sign, U+2212 ISOtech -->
+<!ENTITY lowast CDATA "&#8727;" -- asterisk operator, U+2217 ISOtech -->
+<!ENTITY radic CDATA "&#8730;" -- square root = radical sign,
+ U+221A ISOtech -->
+<!ENTITY prop CDATA "&#8733;" -- proportional to, U+221D ISOtech -->
+<!ENTITY infin CDATA "&#8734;" -- infinity, U+221E ISOtech -->
+<!ENTITY ang CDATA "&#8736;" -- angle, U+2220 ISOamso -->
+<!ENTITY and CDATA "&#8743;" -- logical and = wedge, U+2227 ISOtech -->
+<!ENTITY or CDATA "&#8744;" -- logical or = vee, U+2228 ISOtech -->
+<!ENTITY cap CDATA "&#8745;" -- intersection = cap, U+2229 ISOtech -->
+<!ENTITY cup CDATA "&#8746;" -- union = cup, U+222A ISOtech -->
+<!ENTITY int CDATA "&#8747;" -- integral, U+222B ISOtech -->
+<!ENTITY there4 CDATA "&#8756;" -- therefore, U+2234 ISOtech -->
+<!ENTITY sim CDATA "&#8764;" -- tilde operator = varies with = similar to,
+ U+223C ISOtech -->
+<!-- tilde operator is NOT the same character as the tilde, U+007E,
+ although the same glyph might be used to represent both -->
+<!ENTITY cong CDATA "&#8773;" -- approximately equal to, U+2245 ISOtech -->
+<!ENTITY asymp CDATA "&#8776;" -- almost equal to = asymptotic to,
+ U+2248 ISOamsr -->
+<!ENTITY ne CDATA "&#8800;" -- not equal to, U+2260 ISOtech -->
+<!ENTITY equiv CDATA "&#8801;" -- identical to, U+2261 ISOtech -->
+<!ENTITY le CDATA "&#8804;" -- less-than or equal to, U+2264 ISOtech -->
+<!ENTITY ge CDATA "&#8805;" -- greater-than or equal to,
+ U+2265 ISOtech -->
+<!ENTITY sub CDATA "&#8834;" -- subset of, U+2282 ISOtech -->
+<!ENTITY sup CDATA "&#8835;" -- superset of, U+2283 ISOtech -->
+<!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol
+ font encoding and is not included. Should it be, for symmetry?
+ It is in ISOamsn -->
+<!ENTITY nsub CDATA "&#8836;" -- not a subset of, U+2284 ISOamsn -->
+<!ENTITY sube CDATA "&#8838;" -- subset of or equal to, U+2286 ISOtech -->
+<!ENTITY supe CDATA "&#8839;" -- superset of or equal to,
+ U+2287 ISOtech -->
+<!ENTITY oplus CDATA "&#8853;" -- circled plus = direct sum,
+ U+2295 ISOamsb -->
+<!ENTITY otimes CDATA "&#8855;" -- circled times = vector product,
+ U+2297 ISOamsb -->
+<!ENTITY perp CDATA "&#8869;" -- up tack = orthogonal to = perpendicular,
+ U+22A5 ISOtech -->
+<!ENTITY sdot CDATA "&#8901;" -- dot operator, U+22C5 ISOamsb -->
+<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+
+<!-- Miscellaneous Technical -->
+<!ENTITY lceil CDATA "&#8968;" -- left ceiling = apl upstile,
+ U+2308 ISOamsc -->
+<!ENTITY rceil CDATA "&#8969;" -- right ceiling, U+2309 ISOamsc -->
+<!ENTITY lfloor CDATA "&#8970;" -- left floor = apl downstile,
+ U+230A ISOamsc -->
+<!ENTITY rfloor CDATA "&#8971;" -- right floor, U+230B ISOamsc -->
+<!ENTITY lang CDATA "&#9001;" -- left-pointing angle bracket = bra,
+ U+2329 ISOtech -->
+<!-- lang is NOT the same character as U+003C 'less than'
+ or U+2039 'single left-pointing angle quotation mark' -->
+<!ENTITY rang CDATA "&#9002;" -- right-pointing angle bracket = ket,
+ U+232A ISOtech -->
+<!-- rang is NOT the same character as U+003E 'greater than'
+ or U+203A 'single right-pointing angle quotation mark' -->
+
+<!-- Geometric Shapes -->
+<!ENTITY loz CDATA "&#9674;" -- lozenge, U+25CA ISOpub -->
+
+<!-- Miscellaneous Symbols -->
+<!ENTITY spades CDATA "&#9824;" -- black spade suit, U+2660 ISOpub -->
+<!-- black here seems to mean filled as opposed to hollow -->
+<!ENTITY clubs CDATA "&#9827;" -- black club suit = shamrock,
+ U+2663 ISOpub -->
+<!ENTITY hearts CDATA "&#9829;" -- black heart suit = valentine,
+ U+2665 ISOpub -->
+<!ENTITY diams CDATA "&#9830;" -- black diamond suit, U+2666 ISOpub --> \ No newline at end of file
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..e0d2e2e
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-1"><span class="linenos">1</span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos">3</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..4ecfa2d
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-1"><span class="linenos">1</span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos">3</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..bfa915c
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos">1</span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos">3</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..9ad5369
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos">1</span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos">3</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..09a39e8
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-1"><span class="linenos">1</span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos special">3</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..be1416b
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-1"><span class="linenos">1</span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos special">3</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..eb0e032
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos">1</span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos special">3</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..c984c90
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos">1</span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos special">3</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..1d4de97
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos"> 9</span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..8b34749
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos"> 9</span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..0335a40
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos"> 9</span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..dd9874a
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos"> 9</span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..1821406
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos special"> 9</span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..b403949
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos special"> 9</span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..ec4a0da
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos special"> 9</span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..dd74c64
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_1_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos special"> 9</span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..41aa7be
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-1"><span class="linenos"> </span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos"> </span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..27865cc
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-1"><span class="linenos"> </span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos"> </span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..7fa5d08
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> </span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos"> </span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..b68a33e
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> </span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos"> </span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..22d07f1
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-1"><span class="linenos"> </span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos special"> </span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..4cfdd4c
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-1"><span class="linenos"> </span></a><span class="c1"># a</span>
+<a href="#-2"><span class="linenos">2</span></a><span class="c1"># b</span>
+<a href="#-3"><span class="linenos special"> </span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..0e977e4
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> </span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos special"> </span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..6345020
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> </span><span class="c1"># a</span>
+<span class="linenos">2</span><span class="c1"># b</span>
+<span class="linenos special"> </span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..50b491a
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos"> </span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..561d47f
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos"> </span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..fd7c0f0
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos"> </span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..12276e8
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos"> </span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..38662f9
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos special"> </span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..a07dfcc
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><a href="#-8"><span class="linenos"> 8</span></a><span class="c1"># a</span>
+<a href="#-9"><span class="linenos special"> </span></a><span class="c1"># b</span>
+<a href="#-10"><span class="linenos">10</span></a><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..56bca93
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><span class="filename">testfilename</span><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos special"> </span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..71548a1
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_cls_step_2_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight"><pre><span></span><span class="linenos"> 8</span><span class="c1"># a</span>
+<span class="linenos special"> </span><span class="c1"># b</span>
+<span class="linenos">10</span><span class="c1"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..d9eeca5
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..0913e4f
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..31525d4
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..e1722cf
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..ee18c21
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..628a1df
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..633c3d5
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..2c705ad
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..9c91cf4
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..d39cb2c
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..d3e4818
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..1233674
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..ef26126
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..8a64149
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..3f08277
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..f5ac0bc
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_1_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..3153304
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..47c4a13
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..28ab99c
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..eb6e847
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..ec9d56f
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..3121cd0
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-1"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-2"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-3"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..c4c4c57
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..f003b15
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..26a2dd4
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..bb1be91
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..c8c440d
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..9af5753
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..76a4047
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..aeb6b10
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><a href="#-8"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span></a><span style="color: #3D7B7B; font-style: italic"># a</span>
+<a href="#-9"><span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></a><span style="color: #3D7B7B; font-style: italic"># b</span>
+<a href="#-10"><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></a><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..cb5b02b
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><span class="filename">testfilename</span><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..5e472a1
--- /dev/null
+++ b/tests/html_linenos_expected_output/inline_nocls_step_2_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,4 @@
+<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span><span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span><span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..861165c
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-1">1</a></span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="normal"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..af31392
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-1">1</a></span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="normal"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..a27ddba
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
+<span class="normal">2</span>
+<span class="normal">3</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..b3044e7
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
+<span class="normal">2</span>
+<span class="normal">3</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..ee74013
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-1">1</a></span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="special"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..a4a3f8e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-1">1</a></span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="special"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..f910782
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
+<span class="normal">2</span>
+<span class="special">3</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..e79e436
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
+<span class="normal">2</span>
+<span class="special">3</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..1a396a7
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="normal"><a href="#-9"> 9</a></span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..04f76cb
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="normal"><a href="#-9"> 9</a></span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..99675f0
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="normal"> 9</span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..1d69eaf
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="normal"> 9</span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..9a47c60
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="special"><a href="#-9"> 9</a></span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..03285e9
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="special"><a href="#-9"> 9</a></span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..0205691
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="special"> 9</span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..b94db01
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_1_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="special"> 9</span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..bd13be4
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="normal"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..fcacc18
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="normal"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..4a418e6
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal">2</span>
+<span class="normal"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..7128962
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal">2</span>
+<span class="normal"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..60cab0e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="special"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..848e666
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal"><a href="#-2">2</a></span>
+<span class="special"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..a690bd0
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal">2</span>
+<span class="special"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..31ceb56
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> </span>
+<span class="normal">2</span>
+<span class="special"> </span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..3a750e9
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="normal"> </span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..6837563
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="normal"> </span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..bc0a078
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="normal"> </span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..a8125e8
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="normal"> </span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..36a8e5f
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="special"> </span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..200fffe
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"><a href="#-8"> 8</a></span>
+<span class="special"> </span>
+<span class="normal"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..f9efd12
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="special"> </span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..2d9d86e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_cls_step_2_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 8</span>
+<span class="special"> </span>
+<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># a</span>
+<span class="c1"># b</span>
+<span class="c1"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..1cdc6bb
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-1">1</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..6bad0df
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-1">1</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..ffd88d4
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..62c5a6e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">3</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..e719f8e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-1">1</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..e07cebe
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-1">1</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"><a href="#-3">3</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..fbb6cde
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..ce4bfa8
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">1</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;">3</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..3402b4c
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-9"> 9</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..2eb3396
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-9"> 9</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..51b2dba
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..91f66e1
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 9</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..7e5aadf
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"><a href="#-9"> 9</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..b10fdca
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"><a href="#-9"> 9</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..8067f7e
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..f3fde8b
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_1_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> 9</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_filename.html
new file mode 100644
index 0000000..8be13f0
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..f72b326
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_filename.html
new file mode 100644
index 0000000..512db3b
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..26d6723
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_filename.html
new file mode 100644
index 0000000..0270512
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..d6f4d3c
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-2">2</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_filename.html
new file mode 100644
index 0000000..1f6dc86
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..c606d01
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_1_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">2</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_filename.html
new file mode 100644
index 0000000..b90f2b0
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_nofilename.html
new file mode 100644
index 0000000..926dd8c
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_filename.html
new file mode 100644
index 0000000..1623295
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_nofilename.html
new file mode 100644
index 0000000..0252dce
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_0_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_filename.html
new file mode 100644
index 0000000..791f640
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_nofilename.html
new file mode 100644
index 0000000..bbc20f6
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_anchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-8"> 8</a></span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"><a href="#-10">10</a></span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_filename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_filename.html
new file mode 100644
index 0000000..5c04be3
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_filename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><th class="filename" colspan="2"><span class="filename">testfilename</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_nofilename.html b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_nofilename.html
new file mode 100644
index 0000000..fc75b66
--- /dev/null
+++ b/tests/html_linenos_expected_output/table_nocls_step_2_start_8_special_3_noanchor_nofilename.html
@@ -0,0 +1,6 @@
+<div class="highlight" style="background: #f8f8f8"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;"> 8</span>
+<span style="color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px;"> </span>
+<span style="color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px;">10</span></pre></div></td><td class="code"><div><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># a</span>
+<span style="color: #3D7B7B; font-style: italic"># b</span>
+<span style="color: #3D7B7B; font-style: italic"># c</span>
+</pre></div></td></tr></table></div>
diff --git a/tests/snippets/apacheconf/test_directive_no_args.txt b/tests/snippets/apacheconf/test_directive_no_args.txt
new file mode 100644
index 0000000..a2f3354
--- /dev/null
+++ b/tests/snippets/apacheconf/test_directive_no_args.txt
@@ -0,0 +1,12 @@
+---input---
+Example
+ServerName localhost
+
+---tokens---
+'Example' Name.Builtin
+'\n' Text.Whitespace
+
+'ServerName' Name.Builtin
+' ' Text.Whitespace
+'localhost' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_fix_lock_absolute_path.txt b/tests/snippets/apacheconf/test_fix_lock_absolute_path.txt
new file mode 100644
index 0000000..a274834
--- /dev/null
+++ b/tests/snippets/apacheconf/test_fix_lock_absolute_path.txt
@@ -0,0 +1,8 @@
+---input---
+LockFile /var/lock/apache2/accept.lock
+
+---tokens---
+'LockFile' Name.Builtin
+' ' Text.Whitespace
+'/var/lock/apache2/accept.lock' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_include_globs.txt b/tests/snippets/apacheconf/test_include_globs.txt
new file mode 100644
index 0000000..495242e
--- /dev/null
+++ b/tests/snippets/apacheconf/test_include_globs.txt
@@ -0,0 +1,8 @@
+---input---
+Include /etc/httpd/conf.d/*.conf
+
+---tokens---
+'Include' Name.Builtin
+' ' Text.Whitespace
+'/etc/httpd/conf.d/*.conf' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_malformed_scoped_directive_closing_tag.txt b/tests/snippets/apacheconf/test_malformed_scoped_directive_closing_tag.txt
new file mode 100644
index 0000000..c65628b
--- /dev/null
+++ b/tests/snippets/apacheconf/test_malformed_scoped_directive_closing_tag.txt
@@ -0,0 +1,19 @@
+---input---
+<VirtualHost "test">
+</VirtualHost
+>
+
+---tokens---
+'<VirtualHost' Name.Tag
+' ' Text.Whitespace
+'"test"' Literal.String
+'>' Name.Tag
+'\n' Text.Whitespace
+
+'<' Error
+'/' Error
+'VirtualHost' Name.Builtin
+'\n' Text.Whitespace
+
+'>' Error
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_multi_include_globs.txt b/tests/snippets/apacheconf/test_multi_include_globs.txt
new file mode 100644
index 0000000..abf5bb0
--- /dev/null
+++ b/tests/snippets/apacheconf/test_multi_include_globs.txt
@@ -0,0 +1,8 @@
+---input---
+Include /etc/httpd/conf.d/*/*.conf
+
+---tokens---
+'Include' Name.Builtin
+' ' Text.Whitespace
+'/etc/httpd/conf.d/*/*.conf' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_multi_include_globs_root.txt b/tests/snippets/apacheconf/test_multi_include_globs_root.txt
new file mode 100644
index 0000000..5df63e4
--- /dev/null
+++ b/tests/snippets/apacheconf/test_multi_include_globs_root.txt
@@ -0,0 +1,8 @@
+---input---
+Include /*conf/*.conf
+
+---tokens---
+'Include' Name.Builtin
+' ' Text.Whitespace
+'/*conf/*.conf' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_multiline_argument.txt b/tests/snippets/apacheconf/test_multiline_argument.txt
new file mode 100644
index 0000000..4163970
--- /dev/null
+++ b/tests/snippets/apacheconf/test_multiline_argument.txt
@@ -0,0 +1,20 @@
+---input---
+SecAction \
+ "id:'900001', \
+ phase:1, \
+ t:none, \
+ setvar:tx.critical_anomaly_score=5, \
+ setvar:tx.error_anomaly_score=4, \
+ setvar:tx.warning_anomaly_score=3, \
+ setvar:tx.notice_anomaly_score=2, \
+ nolog, \
+ pass"
+
+---tokens---
+'SecAction' Name.Builtin
+' ' Text.Whitespace
+'\\\n' Text
+
+' ' Text.Whitespace
+'"id:\'900001\', \\\n phase:1, \\\n t:none, \\\n setvar:tx.critical_anomaly_score=5, \\\n setvar:tx.error_anomaly_score=4, \\\n setvar:tx.warning_anomaly_score=3, \\\n setvar:tx.notice_anomaly_score=2, \\\n nolog, \\\n pass"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_multiline_comment.txt b/tests/snippets/apacheconf/test_multiline_comment.txt
new file mode 100644
index 0000000..cdcfe01
--- /dev/null
+++ b/tests/snippets/apacheconf/test_multiline_comment.txt
@@ -0,0 +1,12 @@
+---input---
+#SecAction \
+ "id:'900004', \
+ phase:1, \
+ t:none, \
+ setvar:tx.anomaly_score_blocking=on, \
+ nolog, \
+ pass"
+
+---tokens---
+'#SecAction \\\n "id:\'900004\', \\\n phase:1, \\\n t:none, \\\n setvar:tx.anomaly_score_blocking=on, \\\n nolog, \\\n pass"' Comment
+'\n' Text.Whitespace
diff --git a/tests/snippets/apacheconf/test_normal_scoped_directive.txt b/tests/snippets/apacheconf/test_normal_scoped_directive.txt
new file mode 100644
index 0000000..f00496d
--- /dev/null
+++ b/tests/snippets/apacheconf/test_normal_scoped_directive.txt
@@ -0,0 +1,14 @@
+---input---
+<VirtualHost "test">
+</VirtualHost>
+
+---tokens---
+'<VirtualHost' Name.Tag
+' ' Text.Whitespace
+'"test"' Literal.String
+'>' Name.Tag
+'\n' Text.Whitespace
+
+'</VirtualHost' Name.Tag
+'>' Name.Tag
+'\n' Text.Whitespace
diff --git a/tests/snippets/apl/test_leading_underscore.txt b/tests/snippets/apl/test_leading_underscore.txt
new file mode 100644
index 0000000..c11258d
--- /dev/null
+++ b/tests/snippets/apl/test_leading_underscore.txt
@@ -0,0 +1,26 @@
+---input---
+_op_←{_←⍺ ⍵⋄(⍺⍺ ⍺)+⍵⍵ ⍵}
+
+---tokens---
+'_op_' Name.Variable
+'←' Keyword.Declaration
+'{' Keyword.Type
+'_' Name.Variable
+'←' Keyword.Declaration
+'⍺' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'⍵' Name.Builtin.Pseudo
+'⋄' Punctuation
+'(' Punctuation
+'⍺' Name.Builtin.Pseudo
+'⍺' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'⍺' Name.Builtin.Pseudo
+')' Punctuation
+'+' Operator
+'⍵' Name.Builtin.Pseudo
+'⍵' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'⍵' Name.Builtin.Pseudo
+'}' Keyword.Type
+'\n' Text.Whitespace
diff --git a/tests/snippets/asm/test_cpuid.txt b/tests/snippets/asm/test_cpuid.txt
new file mode 100644
index 0000000..8256618
--- /dev/null
+++ b/tests/snippets/asm/test_cpuid.txt
@@ -0,0 +1,9 @@
+# CPU is a valid directive, and we don't want to parse this as
+# cpu id, but as a single token. See bug #1517
+
+---input---
+cpuid
+
+---tokens---
+'cpuid' Name.Function
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_basic_bst.txt b/tests/snippets/bibtex/test_basic_bst.txt
new file mode 100644
index 0000000..c519ae0
--- /dev/null
+++ b/tests/snippets/bibtex/test_basic_bst.txt
@@ -0,0 +1,54 @@
+---input---
+% BibTeX standard bibliography style `plain'
+
+INTEGERS { output.state before.all }
+
+FUNCTION {sort.format.title}
+{ 't :=
+"A " #2
+ "An " #3
+ "The " #4 t chop.word
+ chop.word
+chop.word
+sortify
+#1 global.max$ substring$
+}
+
+ITERATE {call.type$}
+
+---tokens---
+"% BibTeX standard bibliography style `plain'" Comment
+'\n\n' Text.Whitespace
+
+'INTEGERS { output.state before.all }' Comment
+'\n\n' Text.Whitespace
+
+'FUNCTION {sort.format.title}' Comment
+'\n' Text.Whitespace
+
+"{ 't :=" Comment
+'\n' Text.Whitespace
+
+'"A " #2' Comment
+'\n ' Text.Whitespace
+'"An " #3' Comment
+'\n ' Text.Whitespace
+'"The " #4 t chop.word' Comment
+'\n ' Text.Whitespace
+'chop.word' Comment
+'\n' Text.Whitespace
+
+'chop.word' Comment
+'\n' Text.Whitespace
+
+'sortify' Comment
+'\n' Text.Whitespace
+
+'#1 global.max$ substring$' Comment
+'\n' Text.Whitespace
+
+'}' Comment
+'\n\n' Text.Whitespace
+
+'ITERATE {call.type$}' Comment
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_comment.txt b/tests/snippets/bibtex/test_comment.txt
new file mode 100644
index 0000000..6e98d64
--- /dev/null
+++ b/tests/snippets/bibtex/test_comment.txt
@@ -0,0 +1,7 @@
+---input---
+@COMMENT{test}
+
+---tokens---
+'@COMMENT' Comment
+'{test}' Comment
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_entry.txt b/tests/snippets/bibtex/test_entry.txt
new file mode 100644
index 0000000..c712a2f
--- /dev/null
+++ b/tests/snippets/bibtex/test_entry.txt
@@ -0,0 +1,63 @@
+---input---
+This is a comment.
+
+@ARTICLE{ruckenstein-diffusion,
+ author = "Liu, Hongquin" # and # "Ruckenstein, Eli",
+ year = 1997,
+ month = JAN,
+ pages = "888-895"
+}
+
+---tokens---
+'This is a comment.' Comment
+'\n\n' Text.Whitespace
+
+'@ARTICLE' Name.Class
+'{' Punctuation
+'ruckenstein-diffusion' Name.Label
+',' Punctuation
+'\n ' Text.Whitespace
+'author' Name.Attribute
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'"' Literal.String
+'Liu, Hongquin' Literal.String
+'"' Literal.String
+' ' Text.Whitespace
+'#' Punctuation
+' ' Text.Whitespace
+'and' Name.Variable
+' ' Text.Whitespace
+'#' Punctuation
+' ' Text.Whitespace
+'"' Literal.String
+'Ruckenstein, Eli' Literal.String
+'"' Literal.String
+',' Punctuation
+'\n ' Text.Whitespace
+'year' Name.Attribute
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'1997' Literal.Number
+',' Punctuation
+'\n ' Text.Whitespace
+'month' Name.Attribute
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'JAN' Name.Variable
+',' Punctuation
+'\n ' Text.Whitespace
+'pages' Name.Attribute
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'"' Literal.String
+'888-895' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_mismatched_brace.txt b/tests/snippets/bibtex/test_mismatched_brace.txt
new file mode 100644
index 0000000..6c1deda
--- /dev/null
+++ b/tests/snippets/bibtex/test_mismatched_brace.txt
@@ -0,0 +1,10 @@
+---input---
+@PREAMBLE(""}
+
+---tokens---
+'@PREAMBLE' Name.Class
+'(' Punctuation
+'"' Literal.String
+'"' Literal.String
+'}' Error
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_missing_body.txt b/tests/snippets/bibtex/test_missing_body.txt
new file mode 100644
index 0000000..24dad98
--- /dev/null
+++ b/tests/snippets/bibtex/test_missing_body.txt
@@ -0,0 +1,10 @@
+---input---
+@ARTICLE xxx
+
+---tokens---
+'@ARTICLE' Name.Class
+' ' Text.Whitespace
+'x' Error
+'x' Error
+'x' Error
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_preamble.txt b/tests/snippets/bibtex/test_preamble.txt
new file mode 100644
index 0000000..9625f96
--- /dev/null
+++ b/tests/snippets/bibtex/test_preamble.txt
@@ -0,0 +1,11 @@
+---input---
+@PREAMBLE{"% some LaTeX code here"}
+
+---tokens---
+'@PREAMBLE' Name.Class
+'{' Punctuation
+'"' Literal.String
+'% some LaTeX code here' Literal.String
+'"' Literal.String
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/bibtex/test_string.txt b/tests/snippets/bibtex/test_string.txt
new file mode 100644
index 0000000..f76a31b
--- /dev/null
+++ b/tests/snippets/bibtex/test_string.txt
@@ -0,0 +1,15 @@
+---input---
+@STRING(SCI = "Science")
+
+---tokens---
+'@STRING' Name.Class
+'(' Punctuation
+'SCI' Name.Attribute
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'"' Literal.String
+'Science' Literal.String
+'"' Literal.String
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_comment_end.txt b/tests/snippets/c/test_comment_end.txt
new file mode 100644
index 0000000..2a21d65
--- /dev/null
+++ b/tests/snippets/c/test_comment_end.txt
@@ -0,0 +1,31 @@
+In the past the "*/" was marked as an error to "helpfully"
+indicate a wrong comment end.
+
+---input---
+int m21=((result_0*0+result_1*/*0<-----*/1)%mod+mod)%mod;
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'m21' Name
+'=' Operator
+'(' Punctuation
+'(' Punctuation
+'result_0' Name
+'*' Operator
+'0' Literal.Number.Integer
+'+' Operator
+'result_1' Name
+'*' Operator
+'/*0<-----*/' Comment.Multiline
+'1' Literal.Number.Integer
+')' Punctuation
+'%' Operator
+'mod' Name
+'+' Operator
+'mod' Name
+')' Punctuation
+'%' Operator
+'mod' Name
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_function_comments.txt b/tests/snippets/c/test_function_comments.txt
new file mode 100644
index 0000000..e8d8a69
--- /dev/null
+++ b/tests/snippets/c/test_function_comments.txt
@@ -0,0 +1,409 @@
+---input---
+int func1(int x, int y)
+ /*@requires y >= 0*/
+{
+ return x / y;
+}
+
+
+int func2(int x, int y) //@requires y >= 0;
+{
+ return x / y;
+}
+
+
+void func3()
+//#test{};
+{
+ fun(2,3)//test1;
+ ;
+}
+
+
+int func4(int x, int y)
+ /*@requires y >= 0;*/
+{
+ return x / y;
+}
+
+
+int func5(int x, int y)
+ /*@requires y >= 0
+ {
+ return x / y;
+ }
+ */
+ {
+ return 2;
+ }
+
+
+//@requires y >= 0;
+//@requires y >= 0
+/*
+calling(2,5)
+*/
+/*
+calling(2,5);
+*/
+int func6(int x, int y)
+ //@requires y >= 0
+ //@requires y >= 0;
+ /*
+ hello(2,3);
+ */
+ /*
+ hello(2,3)
+ */
+ {
+ // haha(2,3);
+ return x / y;
+ /*
+ callblabla(x, y);
+ */
+ }
+//@requires y >= 0;
+//@requires y >= 0
+/*
+calling(2,5)
+*/
+/*
+calling(2,5);
+*/
+
+
+int * //@# a pointer to int
+func7 /* @# why a comment here? */ (
+ int /* the index has to be an int */ a, // index into the array
+ int *b //the array @!
+)
+/*
+ The end of the func params @ (@ will result error if parsed incorrectly)
+*/
+{
+ // yet another comment
+ return b[a];
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'func1' Name.Function
+'(' Punctuation
+'int' Keyword.Type
+' ' Text.Whitespace
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'/*@requires y >= 0*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'x' Name
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'y' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'func2' Name.Function
+'(' Punctuation
+'int' Keyword.Type
+' ' Text.Whitespace
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'y' Name
+')' Punctuation
+' ' Text.Whitespace
+'//@requires y >= 0;\n' Comment.Single
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'x' Name
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'y' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'void' Keyword.Type
+' ' Text.Whitespace
+'func3' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'//#test{};\n' Comment.Single
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'fun' Name
+'(' Punctuation
+'2' Literal.Number.Integer
+',' Punctuation
+'3' Literal.Number.Integer
+')' Punctuation
+'//test1;\n' Comment.Single
+
+' ' Text.Whitespace
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'func4' Name.Function
+'(' Punctuation
+'int' Keyword.Type
+' ' Text.Whitespace
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'/*@requires y >= 0;*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'x' Name
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'y' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'func5' Name.Function
+'(' Punctuation
+'int' Keyword.Type
+' ' Text.Whitespace
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'/*@requires y >= 0\n {\n return x / y;\n }\n */' Comment.Multiline
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'//@requires y >= 0;\n' Comment.Single
+
+'//@requires y >= 0\n' Comment.Single
+
+'/*\ncalling(2,5)\n*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'/*\ncalling(2,5);\n*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'func6' Name.Function
+'(' Punctuation
+'int' Keyword.Type
+' ' Text.Whitespace
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'//@requires y >= 0\n' Comment.Single
+
+' ' Text.Whitespace
+'//@requires y >= 0;\n' Comment.Single
+
+' ' Text.Whitespace
+'/*\n hello(2,3);\n */' Comment.Multiline
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'/*\n hello(2,3)\n */' Comment.Multiline
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'// haha(2,3);\n' Comment.Single
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'x' Name
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'y' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'/*\n callblabla(x, y);\n */' Comment.Multiline
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'//@requires y >= 0;\n' Comment.Single
+
+'//@requires y >= 0\n' Comment.Single
+
+'/*\ncalling(2,5)\n*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'/*\ncalling(2,5);\n*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'*' Operator
+' ' Text.Whitespace
+'//@# a pointer to int\n' Comment.Single
+
+'func7' Name.Function
+' ' Text.Whitespace
+'/* @# why a comment here? */' Comment.Multiline
+' ' Text.Whitespace
+'(' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'/* the index has to be an int */' Comment.Multiline
+' ' Text.Whitespace
+'a' Name
+',' Punctuation
+' ' Text.Whitespace
+'// index into the array\n' Comment.Single
+
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'*' Operator
+'b' Name
+' ' Text.Whitespace
+'//the array @!\n' Comment.Single
+
+')' Punctuation
+'\n' Text.Whitespace
+
+'/*\n The end of the func params @ (@ will result error if parsed incorrectly)\n*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'// yet another comment\n' Comment.Single
+
+' ' Text.Whitespace
+'return' Keyword
+' ' Text.Whitespace
+'b' Name
+'[' Punctuation
+'a' Name
+']' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_label.txt b/tests/snippets/c/test_label.txt
new file mode 100644
index 0000000..6f4ee80
--- /dev/null
+++ b/tests/snippets/c/test_label.txt
@@ -0,0 +1,31 @@
+---input---
+int main()
+{
+foo:
+ goto foo;
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+'foo' Name.Label
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'goto' Keyword
+' ' Text.Whitespace
+'foo' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_label_followed_by_statement.txt b/tests/snippets/c/test_label_followed_by_statement.txt
new file mode 100644
index 0000000..6dc8468
--- /dev/null
+++ b/tests/snippets/c/test_label_followed_by_statement.txt
@@ -0,0 +1,35 @@
+---input---
+int main()
+{
+foo:return 0;
+ goto foo;
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+'foo' Name.Label
+':' Punctuation
+'return' Keyword
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'goto' Keyword
+' ' Text.Whitespace
+'foo' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_label_space_before_colon.txt b/tests/snippets/c/test_label_space_before_colon.txt
new file mode 100644
index 0000000..2c3d065
--- /dev/null
+++ b/tests/snippets/c/test_label_space_before_colon.txt
@@ -0,0 +1,32 @@
+---input---
+int main()
+{
+foo :
+ goto foo;
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+'foo' Name.Label
+' ' Text.Whitespace
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'goto' Keyword
+' ' Text.Whitespace
+'foo' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_numbers.txt b/tests/snippets/c/test_numbers.txt
new file mode 100644
index 0000000..75af6fd
--- /dev/null
+++ b/tests/snippets/c/test_numbers.txt
@@ -0,0 +1,20 @@
+---input---
+42 23.42 23. .42 023 0xdeadbeef 23e+42 42e-23
+
+---tokens---
+'42' Literal.Number.Integer
+' ' Text.Whitespace
+'23.42' Literal.Number.Float
+' ' Text.Whitespace
+'23.' Literal.Number.Float
+' ' Text.Whitespace
+'.42' Literal.Number.Float
+' ' Text.Whitespace
+'023' Literal.Number.Oct
+' ' Text.Whitespace
+'0xdeadbeef' Literal.Number.Hex
+' ' Text.Whitespace
+'23e+42' Literal.Number.Float
+' ' Text.Whitespace
+'42e-23' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_preproc_file.txt b/tests/snippets/c/test_preproc_file.txt
new file mode 100644
index 0000000..2b5449e
--- /dev/null
+++ b/tests/snippets/c/test_preproc_file.txt
@@ -0,0 +1,17 @@
+---input---
+#include <foo>
+# include <foo>
+
+---tokens---
+'#' Comment.Preproc
+'include' Comment.Preproc
+' ' Text.Whitespace
+'<foo>' Comment.PreprocFile
+'\n' Comment.Preproc
+
+'#' Comment.Preproc
+' ' Text.Whitespace
+'include' Comment.Preproc
+' ' Text.Whitespace
+'<foo>' Comment.PreprocFile
+'\n' Comment.Preproc
diff --git a/tests/snippets/c/test_preproc_file2.txt b/tests/snippets/c/test_preproc_file2.txt
new file mode 100644
index 0000000..5d6ef3b
--- /dev/null
+++ b/tests/snippets/c/test_preproc_file2.txt
@@ -0,0 +1,17 @@
+---input---
+#include "foo.h"
+# include "foo.h"
+
+---tokens---
+'#' Comment.Preproc
+'include' Comment.Preproc
+' ' Text.Whitespace
+'"foo.h"' Comment.PreprocFile
+'\n' Comment.Preproc
+
+'#' Comment.Preproc
+' ' Text.Whitespace
+'include' Comment.Preproc
+' ' Text.Whitespace
+'"foo.h"' Comment.PreprocFile
+'\n' Comment.Preproc
diff --git a/tests/snippets/c/test_preproc_file3.txt b/tests/snippets/c/test_preproc_file3.txt
new file mode 100644
index 0000000..b36db4c
--- /dev/null
+++ b/tests/snippets/c/test_preproc_file3.txt
@@ -0,0 +1,18 @@
+Space around line break before macro is valid C, but failed to parse previously.
+
+---input---
+foo();
+ #define FOO 0
+
+---tokens---
+'foo' Name
+'(' Punctuation
+')' Punctuation
+';' Punctuation
+' ' Text.Whitespace
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'#' Comment.Preproc
+'define FOO 0' Comment.Preproc
+'\n' Comment.Preproc
diff --git a/tests/snippets/c/test_preproc_file4.txt b/tests/snippets/c/test_preproc_file4.txt
new file mode 100644
index 0000000..db1592d
--- /dev/null
+++ b/tests/snippets/c/test_preproc_file4.txt
@@ -0,0 +1,13 @@
+Comments can precede preprocessor macros
+
+---input---
+/* Comment */ /* Another */ #define FOO 0
+
+---tokens---
+'/* Comment */' Comment.Multiline
+' ' Text.Whitespace
+'/* Another */' Comment.Multiline
+' ' Text.Whitespace
+'#' Comment.Preproc
+'define FOO 0' Comment.Preproc
+'\n' Comment.Preproc
diff --git a/tests/snippets/c/test_preproc_file5.txt b/tests/snippets/c/test_preproc_file5.txt
new file mode 100644
index 0000000..f4a727b
--- /dev/null
+++ b/tests/snippets/c/test_preproc_file5.txt
@@ -0,0 +1,19 @@
+Preprocessor macros should appear only at the beginning of the line.
+Otherwise we should produce an error token.
+
+---input---
+foo(); #define FOO 0
+
+---tokens---
+'foo' Name
+'(' Punctuation
+')' Punctuation
+';' Punctuation
+' ' Text.Whitespace
+'#' Error
+'define' Name
+' ' Text.Whitespace
+'FOO' Name
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_string_resembling_decl_end.txt b/tests/snippets/c/test_string_resembling_decl_end.txt
new file mode 100644
index 0000000..17ce223
--- /dev/null
+++ b/tests/snippets/c/test_string_resembling_decl_end.txt
@@ -0,0 +1,41 @@
+---input---
+// This should not be recognized as a function declaration followed by
+// garbage.
+string xyz(");");
+
+// This should not be recognized as a function definition.
+
+string xyz("){ }");
+
+---tokens---
+'// This should not be recognized as a function declaration followed by\n' Comment.Single
+
+'// garbage.\n' Comment.Single
+
+'string' Name
+' ' Text.Whitespace
+'xyz' Name
+'(' Punctuation
+'"' Literal.String
+');' Literal.String
+'"' Literal.String
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'// This should not be recognized as a function definition.\n' Comment.Single
+
+'\n' Text.Whitespace
+
+'string' Name
+' ' Text.Whitespace
+'xyz' Name
+'(' Punctuation
+'"' Literal.String
+'){ }' Literal.String
+'"' Literal.String
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_switch.txt b/tests/snippets/c/test_switch.txt
new file mode 100644
index 0000000..d1c1148
--- /dev/null
+++ b/tests/snippets/c/test_switch.txt
@@ -0,0 +1,56 @@
+---input---
+int main()
+{
+ switch (0)
+ {
+ case 0:
+ default:
+ ;
+ }
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'switch' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'0' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'case' Keyword
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'default' Keyword
+':' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/c/test_switch_space_before_colon.txt b/tests/snippets/c/test_switch_space_before_colon.txt
new file mode 100644
index 0000000..583f77c
--- /dev/null
+++ b/tests/snippets/c/test_switch_space_before_colon.txt
@@ -0,0 +1,58 @@
+---input---
+int main()
+{
+ switch (0)
+ {
+ case 0 :
+ default :
+ ;
+ }
+}
+
+---tokens---
+'int' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'switch' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'0' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'case' Keyword
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+' ' Text.Whitespace
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'default' Keyword
+' ' Text.Whitespace
+':' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/cfm/test_basic_comment.txt b/tests/snippets/cfm/test_basic_comment.txt
new file mode 100644
index 0000000..c07a72a
--- /dev/null
+++ b/tests/snippets/cfm/test_basic_comment.txt
@@ -0,0 +1,8 @@
+---input---
+<!--- cfcomment --->
+
+---tokens---
+'<!---' Comment.Multiline
+' cfcomment ' Comment.Multiline
+'--->' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/cfm/test_nested_comment.txt b/tests/snippets/cfm/test_nested_comment.txt
new file mode 100644
index 0000000..f8aaf4c
--- /dev/null
+++ b/tests/snippets/cfm/test_nested_comment.txt
@@ -0,0 +1,12 @@
+---input---
+<!--- nested <!--- cfcomment ---> --->
+
+---tokens---
+'<!---' Comment.Multiline
+' nested ' Comment.Multiline
+'<!---' Comment.Multiline
+' cfcomment ' Comment.Multiline
+'--->' Comment.Multiline
+' ' Comment.Multiline
+'--->' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/coffeescript/test_beware_infinite_loop.txt b/tests/snippets/coffeescript/test_beware_infinite_loop.txt
new file mode 100644
index 0000000..886b706
--- /dev/null
+++ b/tests/snippets/coffeescript/test_beware_infinite_loop.txt
@@ -0,0 +1,14 @@
+# This demonstrates the case that "This isn't really guarding" comment
+# refers to.
+
+---input---
+/a/x;
+
+---tokens---
+'' Text
+'/' Operator
+'a' Name.Other
+'/' Operator
+'x' Name.Other
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/coffeescript/test_mixed_slashes.txt b/tests/snippets/coffeescript/test_mixed_slashes.txt
new file mode 100644
index 0000000..8701fad
--- /dev/null
+++ b/tests/snippets/coffeescript/test_mixed_slashes.txt
@@ -0,0 +1,13 @@
+---input---
+a?/foo/:1/2;
+
+---tokens---
+'a' Name.Other
+'?' Operator
+'/foo/' Literal.String.Regex
+':' Operator
+'1' Literal.Number.Integer
+'/' Operator
+'2' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/conftest.py b/tests/snippets/conftest.py
new file mode 100644
index 0000000..02e6255
--- /dev/null
+++ b/tests/snippets/conftest.py
@@ -0,0 +1,32 @@
+"""
+ Generated lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Checks that lexers output the expected tokens for each sample
+ under lexers/*/test_*.txt.
+
+ After making a change, rather than updating the samples manually,
+ run `pytest --update-goldens tests/lexers`.
+
+ To add a new sample, create a new file matching this pattern.
+ The directory must match the alias of the lexer to be used.
+ Populate only the input, then just `--update-goldens`.
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pathlib
+import pytest
+
+from tests.conftest import LexerInlineTestItem
+
+
+def pytest_collect_file(parent, path):
+ if path.ext == '.txt':
+ return LexerTestFile.from_parent(parent, path=pathlib.Path(path))
+
+
+class LexerTestFile(pytest.File):
+ def collect(self):
+ yield LexerInlineTestItem.from_parent(self, name='')
diff --git a/tests/snippets/console/fake_ps2_prompt.txt b/tests/snippets/console/fake_ps2_prompt.txt
new file mode 100644
index 0000000..683b36d
--- /dev/null
+++ b/tests/snippets/console/fake_ps2_prompt.txt
@@ -0,0 +1,14 @@
+# Test that missing backslash means it's no prompt.
+
+---input---
+$ echo "> not a prompt"
+> not a prompt
+
+---tokens---
+'$ ' Generic.Prompt
+'echo' Name.Builtin
+' ' Text.Whitespace
+'"> not a prompt"' Literal.String.Double
+'\n' Text.Whitespace
+
+'> not a prompt\n' Generic.Output
diff --git a/tests/snippets/console/prompt_in_output.txt b/tests/snippets/console/prompt_in_output.txt
new file mode 100644
index 0000000..993bc75
--- /dev/null
+++ b/tests/snippets/console/prompt_in_output.txt
@@ -0,0 +1,21 @@
+# Test that output that looks like a prompt is not detected as such.
+
+---input---
+$ cat \
+> test.txt
+line1
+> file content, not prompt!
+
+---tokens---
+'$ ' Generic.Prompt
+'cat' Text
+' ' Text.Whitespace
+'\\\n' Literal.String.Escape
+
+'> ' Generic.Prompt
+'test.txt' Text
+'\n' Text.Whitespace
+
+'line1\n' Generic.Output
+
+'> file content, not prompt!\n' Generic.Output
diff --git a/tests/snippets/console/ps2_prompt.txt b/tests/snippets/console/ps2_prompt.txt
new file mode 100644
index 0000000..175a473
--- /dev/null
+++ b/tests/snippets/console/ps2_prompt.txt
@@ -0,0 +1,15 @@
+---input---
+$ ls\
+> /does/not/exist
+ls: cannot access ...
+
+---tokens---
+'$ ' Generic.Prompt
+'ls' Text
+'\\\n' Literal.String.Escape
+
+'> ' Generic.Prompt
+'/does/not/exist' Text
+'\n' Text.Whitespace
+
+'ls: cannot access ...\n' Generic.Output
diff --git a/tests/snippets/console/test_comment_after_prompt.txt b/tests/snippets/console/test_comment_after_prompt.txt
new file mode 100644
index 0000000..f115715
--- /dev/null
+++ b/tests/snippets/console/test_comment_after_prompt.txt
@@ -0,0 +1,6 @@
+---input---
+$# comment
+
+---tokens---
+'$' Generic.Prompt
+'# comment\n' Comment.Single
diff --git a/tests/snippets/console/test_newline_in_echo_no_ps2.txt b/tests/snippets/console/test_newline_in_echo_no_ps2.txt
new file mode 100644
index 0000000..57a1190
--- /dev/null
+++ b/tests/snippets/console/test_newline_in_echo_no_ps2.txt
@@ -0,0 +1,16 @@
+---input---
+$ echo \
+ hi
+hi
+
+---tokens---
+'$ ' Generic.Prompt
+'echo' Name.Builtin
+' ' Text.Whitespace
+'\\\n' Literal.String.Escape
+
+' ' Text.Whitespace
+'hi' Text
+'\n' Text.Whitespace
+
+'hi\n' Generic.Output
diff --git a/tests/snippets/console/test_newline_in_echo_ps2.txt b/tests/snippets/console/test_newline_in_echo_ps2.txt
new file mode 100644
index 0000000..b90eead
--- /dev/null
+++ b/tests/snippets/console/test_newline_in_echo_ps2.txt
@@ -0,0 +1,16 @@
+---input---
+$ echo \
+> hi
+hi
+
+---tokens---
+'$ ' Generic.Prompt
+'echo' Name.Builtin
+' ' Text.Whitespace
+'\\\n' Literal.String.Escape
+
+'> ' Generic.Prompt
+'hi' Text
+'\n' Text.Whitespace
+
+'hi\n' Generic.Output
diff --git a/tests/snippets/console/test_newline_in_ls_no_ps2.txt b/tests/snippets/console/test_newline_in_ls_no_ps2.txt
new file mode 100644
index 0000000..3366bc0
--- /dev/null
+++ b/tests/snippets/console/test_newline_in_ls_no_ps2.txt
@@ -0,0 +1,16 @@
+---input---
+$ ls \
+ hi
+hi
+
+---tokens---
+'$ ' Generic.Prompt
+'ls' Text
+' ' Text.Whitespace
+'\\\n' Literal.String.Escape
+
+' ' Text.Whitespace
+'hi' Text
+'\n' Text.Whitespace
+
+'hi\n' Generic.Output
diff --git a/tests/snippets/console/test_newline_in_ls_ps2.txt b/tests/snippets/console/test_newline_in_ls_ps2.txt
new file mode 100644
index 0000000..bf1bae5
--- /dev/null
+++ b/tests/snippets/console/test_newline_in_ls_ps2.txt
@@ -0,0 +1,16 @@
+---input---
+$ ls \
+> hi
+hi
+
+---tokens---
+'$ ' Generic.Prompt
+'ls' Text
+' ' Text.Whitespace
+'\\\n' Literal.String.Escape
+
+'> ' Generic.Prompt
+'hi' Text
+'\n' Text.Whitespace
+
+'hi\n' Generic.Output
diff --git a/tests/snippets/console/test_virtualenv.txt b/tests/snippets/console/test_virtualenv.txt
new file mode 100644
index 0000000..420b07a
--- /dev/null
+++ b/tests/snippets/console/test_virtualenv.txt
@@ -0,0 +1,11 @@
+---input---
+(env) [~/project]$ foo -h
+
+---tokens---
+'(env)' Generic.Prompt.VirtualEnv
+' ' Text
+'[~/project]$ ' Generic.Prompt
+'foo' Text
+' ' Text.Whitespace
+'-h' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/coq/test_unicode.txt b/tests/snippets/coq/test_unicode.txt
new file mode 100644
index 0000000..2007f7d
--- /dev/null
+++ b/tests/snippets/coq/test_unicode.txt
@@ -0,0 +1,15 @@
+---input---
+Check (α ≻ β).
+
+---tokens---
+'Check' Keyword.Namespace
+' ' Text
+'(' Operator
+'α' Name
+' ' Text
+'≻' Name.Builtin.Pseudo
+' ' Text
+'β' Name
+')' Operator
+'.' Operator
+'\n' Text
diff --git a/tests/snippets/cpp/test_good_comment.txt b/tests/snippets/cpp/test_good_comment.txt
new file mode 100644
index 0000000..115516d
--- /dev/null
+++ b/tests/snippets/cpp/test_good_comment.txt
@@ -0,0 +1,6 @@
+---input---
+/* foo */
+
+---tokens---
+'/* foo */' Comment.Multiline
+'\n' Text.Whitespace
diff --git a/tests/snippets/cpp/test_open_comment.txt b/tests/snippets/cpp/test_open_comment.txt
new file mode 100644
index 0000000..3799214
--- /dev/null
+++ b/tests/snippets/cpp/test_open_comment.txt
@@ -0,0 +1,5 @@
+---input---
+/* foo
+
+---tokens---
+'/* foo\n' Comment.Multiline
diff --git a/tests/snippets/cpp/test_unicode_identifiers.txt b/tests/snippets/cpp/test_unicode_identifiers.txt
new file mode 100644
index 0000000..c2f3e03
--- /dev/null
+++ b/tests/snippets/cpp/test_unicode_identifiers.txt
@@ -0,0 +1,146 @@
+---input---
+namespace 𝐨𝐩𝐭𝐢𝐨𝐧 {
+ int _hello();
+}
+
+int cześć = 2;
+
+class 𝐨𝐩𝐭𝐢𝐨𝐧𝐬 final {
+ 𝐨𝐩𝐭𝐢𝐨𝐧𝐬() {
+ 爴:
+ int a = 𝐨𝐩𝐭𝐢𝐨𝐧::hello();
+ };
+
+ static 𝐨𝐩𝐭𝐢𝐨𝐧𝐬 𝔡𝔢𝔣𝔞𝔲𝔩𝔱;
+ static 𝐨𝐩𝐭𝐢𝐨𝐧𝐬 𝔢𝔵𝔠𝔢𝔭𝔱𝔦𝔬𝔫𝔰;
+};
+
+enum class ⅭⅤ { red, green = 15, blue };
+
+---tokens---
+'namespace' Keyword
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧' Name.Namespace
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'_hello' Name.Function
+'(' Punctuation
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'int' Keyword.Type
+' ' Text.Whitespace
+'cześć' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'class' Keyword
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧𝐬' Name.Class
+' ' Text.Whitespace
+'final' Keyword
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧𝐬' Name
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'爴' Name.Label
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧' Name
+':' Operator
+':' Operator
+'hello' Name
+'(' Punctuation
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'static' Keyword
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧𝐬' Name
+' ' Text.Whitespace
+'𝔡𝔢𝔣𝔞𝔲𝔩𝔱' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'static' Keyword
+' ' Text.Whitespace
+'𝐨𝐩𝐭𝐢𝐨𝐧𝐬' Name
+' ' Text.Whitespace
+'𝔢𝔵𝔠𝔢𝔭𝔱𝔦𝔬𝔫𝔰' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'enum' Keyword
+' ' Text.Whitespace
+'class' Keyword
+' ' Text.Whitespace
+'ⅭⅤ' Name.Class
+' ' Text.Whitespace
+'{' Punctuation
+' ' Text.Whitespace
+'red' Name
+',' Punctuation
+' ' Text.Whitespace
+'green' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'15' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'blue' Name
+' ' Text.Whitespace
+'}' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_annotation.txt b/tests/snippets/crystal/test_annotation.txt
new file mode 100644
index 0000000..b585fa1
--- /dev/null
+++ b/tests/snippets/crystal/test_annotation.txt
@@ -0,0 +1,16 @@
+---input---
+@[FOO::Bar::Baz(opt: "xx")]
+
+---tokens---
+'@[' Operator
+'FOO::Bar::Baz' Name.Decorator
+'(' Punctuation
+'opt' Literal.String.Symbol
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'xx' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+']' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_array_access.txt b/tests/snippets/crystal/test_array_access.txt
new file mode 100644
index 0000000..6b36c51
--- /dev/null
+++ b/tests/snippets/crystal/test_array_access.txt
@@ -0,0 +1,11 @@
+---input---
+[5][5]?
+
+---tokens---
+'[' Operator
+'5' Literal.Number.Integer
+']' Operator
+'[' Operator
+'5' Literal.Number.Integer
+']?' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_chars.txt b/tests/snippets/crystal/test_chars.txt
new file mode 100644
index 0000000..c254bfb
--- /dev/null
+++ b/tests/snippets/crystal/test_chars.txt
@@ -0,0 +1,25 @@
+---input---
+'a'
+'я'
+'\u{1234}'
+'
+'
+'abc'
+
+---tokens---
+"'a'" Literal.String.Char
+'\n' Text.Whitespace
+
+"'я'" Literal.String.Char
+'\n' Text.Whitespace
+
+"'\\u{1234}'" Literal.String.Char
+'\n' Text.Whitespace
+
+"'\n'" Literal.String.Char
+'\n' Text.Whitespace
+
+"'" Error
+'abc' Name
+"'" Error
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_constant_and_module.txt b/tests/snippets/crystal/test_constant_and_module.txt
new file mode 100644
index 0000000..f8d33ff
--- /dev/null
+++ b/tests/snippets/crystal/test_constant_and_module.txt
@@ -0,0 +1,14 @@
+---input---
+HTTP
+HTTP::Server.new
+
+---tokens---
+'HTTP' Name.Constant
+'\n' Text.Whitespace
+
+'HTTP' Name
+'::' Operator
+'Server' Name
+'.' Operator
+'new' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_escaped_bracestring.txt b/tests/snippets/crystal/test_escaped_bracestring.txt
new file mode 100644
index 0000000..14718b9
--- /dev/null
+++ b/tests/snippets/crystal/test_escaped_bracestring.txt
@@ -0,0 +1,19 @@
+---input---
+str.gsub(%r{\\\\}, "/")
+
+---tokens---
+'str' Name
+'.' Operator
+'gsub' Name
+'(' Punctuation
+'%r{' Literal.String.Regex
+'\\\\' Literal.String.Regex
+'\\\\' Literal.String.Regex
+'}' Literal.String.Regex
+',' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'/' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_escaped_interpolation.txt b/tests/snippets/crystal/test_escaped_interpolation.txt
new file mode 100644
index 0000000..c623464
--- /dev/null
+++ b/tests/snippets/crystal/test_escaped_interpolation.txt
@@ -0,0 +1,9 @@
+---input---
+"\#{a + b}"
+
+---tokens---
+'"' Literal.String.Double
+'\\#' Literal.String.Escape
+'{a + b}' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_interpolation_nested_curly.txt b/tests/snippets/crystal/test_interpolation_nested_curly.txt
new file mode 100644
index 0000000..f4a69f7
--- /dev/null
+++ b/tests/snippets/crystal/test_interpolation_nested_curly.txt
@@ -0,0 +1,56 @@
+---input---
+"A#{ (3..5).group_by { |x| x/2}.map do |k,v| "#{k}" end.join }" + "Z"
+
+---tokens---
+'"' Literal.String.Double
+'A' Literal.String.Double
+'#{' Literal.String.Interpol
+' ' Text.Whitespace
+'(' Punctuation
+'3' Literal.Number.Integer
+'..' Operator
+'5' Literal.Number.Integer
+')' Punctuation
+'.' Operator
+'group_by' Name
+' ' Text.Whitespace
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'|' Operator
+'x' Name
+'|' Operator
+' ' Text.Whitespace
+'x' Name
+'/' Operator
+'2' Literal.Number.Integer
+'}' Literal.String.Interpol
+'.' Operator
+'map' Name
+' ' Text.Whitespace
+'do' Keyword
+' ' Text.Whitespace
+'|' Operator
+'k' Name
+',' Punctuation
+'v' Name
+'|' Operator
+' ' Text.Whitespace
+'"' Literal.String.Double
+'#{' Literal.String.Interpol
+'k' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text.Whitespace
+'end' Keyword
+'.' Operator
+'join' Name
+' ' Text.Whitespace
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'"' Literal.String.Double
+'Z' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_lib.txt b/tests/snippets/crystal/test_lib.txt
new file mode 100644
index 0000000..6f6f107
--- /dev/null
+++ b/tests/snippets/crystal/test_lib.txt
@@ -0,0 +1,58 @@
+---input---
+@[Link("some")]
+lib LibSome
+@[CallConvention("X86_StdCall")]
+fun foo="some.foo"(thing : Void*) : LibC::Int
+end
+
+---tokens---
+'@[' Operator
+'Link' Name.Decorator
+'(' Punctuation
+'"' Literal.String.Double
+'some' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+']' Operator
+'\n' Text.Whitespace
+
+'lib' Keyword
+' ' Text.Whitespace
+'LibSome' Name.Namespace
+'\n' Text.Whitespace
+
+'@[' Operator
+'CallConvention' Name.Decorator
+'(' Punctuation
+'"' Literal.String.Double
+'X86_StdCall' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+']' Operator
+'\n' Text.Whitespace
+
+'fun' Keyword
+' ' Text.Whitespace
+'foo' Name.Function
+'=' Operator
+'"' Literal.String.Double
+'some.foo' Literal.String.Double
+'"' Literal.String.Double
+'(' Punctuation
+'thing' Name
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'Void' Name
+'*' Operator
+')' Punctuation
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'LibC' Name
+'::' Operator
+'Int' Name
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_macro.txt b/tests/snippets/crystal/test_macro.txt
new file mode 100644
index 0000000..765caa2
--- /dev/null
+++ b/tests/snippets/crystal/test_macro.txt
@@ -0,0 +1,76 @@
+---input---
+def<=>(other : self) : Int
+{%for field in %w(first_name middle_name last_name)%}
+cmp={{field.id}}<=>other.{{field.id}}
+return cmp if cmp!=0
+{%end%}
+0
+end
+
+---tokens---
+'def' Keyword
+'<=>' Name.Function
+'(' Punctuation
+'other' Name
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'self' Keyword
+')' Punctuation
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'Int' Name
+'\n' Text.Whitespace
+
+'{%' Literal.String.Interpol
+'for' Keyword
+' ' Text.Whitespace
+'field' Name
+' ' Text.Whitespace
+'in' Keyword
+' ' Text.Whitespace
+'%w(' Literal.String.Other
+'first_name middle_name last_name' Literal.String.Other
+')' Literal.String.Other
+'%}' Literal.String.Interpol
+'\n' Text.Whitespace
+
+'cmp' Name
+'=' Operator
+'{{' Literal.String.Interpol
+'field' Name
+'.' Operator
+'id' Name
+'}}' Literal.String.Interpol
+'<=>' Operator
+'other' Name
+'.' Operator
+'{{' Literal.String.Interpol
+'field' Name
+'.' Operator
+'id' Name
+'}}' Literal.String.Interpol
+'\n' Text.Whitespace
+
+'return' Keyword
+' ' Text.Whitespace
+'cmp' Name
+' ' Text.Whitespace
+'if' Keyword
+' ' Text.Whitespace
+'cmp' Name
+'!=' Operator
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'{%' Literal.String.Interpol
+'end' Keyword
+'%}' Literal.String.Interpol
+'\n' Text.Whitespace
+
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_operator_methods.txt b/tests/snippets/crystal/test_operator_methods.txt
new file mode 100644
index 0000000..084771f
--- /dev/null
+++ b/tests/snippets/crystal/test_operator_methods.txt
@@ -0,0 +1,18 @@
+---input---
+([] of Int32).[]?(5)
+
+---tokens---
+'(' Punctuation
+'[' Operator
+']' Operator
+' ' Text.Whitespace
+'of' Keyword
+' ' Text.Whitespace
+'Int32' Name
+')' Punctuation
+'.' Operator
+'[]?' Name.Operator
+'(' Punctuation
+'5' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_percent_strings.txt b/tests/snippets/crystal/test_percent_strings.txt
new file mode 100644
index 0000000..80f247c
--- /dev/null
+++ b/tests/snippets/crystal/test_percent_strings.txt
@@ -0,0 +1,41 @@
+---input---
+%(hello ("world"))
+%[hello ["world"]]
+%{hello "world"}
+%<hello <"world">>
+%|hello "world"|
+
+---tokens---
+'%(' Literal.String.Other
+'hello ' Literal.String.Other
+'(' Literal.String.Other
+'"world"' Literal.String.Other
+')' Literal.String.Other
+')' Literal.String.Other
+'\n' Text.Whitespace
+
+'%[' Literal.String.Other
+'hello ' Literal.String.Other
+'[' Literal.String.Other
+'"world"' Literal.String.Other
+']' Literal.String.Other
+']' Literal.String.Other
+'\n' Text.Whitespace
+
+'%{' Literal.String.Other
+'hello "world"' Literal.String.Other
+'}' Literal.String.Other
+'\n' Text.Whitespace
+
+'%<' Literal.String.Other
+'hello ' Literal.String.Other
+'<' Literal.String.Other
+'"world"' Literal.String.Other
+'>' Literal.String.Other
+'>' Literal.String.Other
+'\n' Text.Whitespace
+
+'%|' Literal.String.Other
+'hello "world"' Literal.String.Other
+'|' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_percent_strings_special.txt b/tests/snippets/crystal/test_percent_strings_special.txt
new file mode 100644
index 0000000..4ca1c0b
--- /dev/null
+++ b/tests/snippets/crystal/test_percent_strings_special.txt
@@ -0,0 +1,31 @@
+---input---
+%Q(hello \n #{name})
+%q(hello \n #{name})
+%w(foo\nbar baz)
+
+---tokens---
+'%Q(' Literal.String.Other
+'hello ' Literal.String.Other
+'\\n' Literal.String.Escape
+' ' Literal.String.Other
+'#{' Literal.String.Interpol
+'name' Name
+'}' Literal.String.Interpol
+')' Literal.String.Other
+'\n' Text.Whitespace
+
+'%q(' Literal.String.Other
+'hello ' Literal.String.Other
+'\\' Literal.String.Other
+'n ' Literal.String.Other
+'#' Literal.String.Other
+'{name}' Literal.String.Other
+')' Literal.String.Other
+'\n' Text.Whitespace
+
+'%w(' Literal.String.Other
+'foo' Literal.String.Other
+'\\' Literal.String.Other
+'nbar baz' Literal.String.Other
+')' Literal.String.Other
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_pseudo_builtins.txt b/tests/snippets/crystal/test_pseudo_builtins.txt
new file mode 100644
index 0000000..9c8c917
--- /dev/null
+++ b/tests/snippets/crystal/test_pseudo_builtins.txt
@@ -0,0 +1,20 @@
+---input---
+record Cls do
+def_equals s
+end
+
+---tokens---
+'record' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'Cls' Name
+' ' Text.Whitespace
+'do' Keyword
+'\n' Text.Whitespace
+
+'def_equals' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'s' Name
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_pseudo_keywords.txt b/tests/snippets/crystal/test_pseudo_keywords.txt
new file mode 100644
index 0000000..18827ad
--- /dev/null
+++ b/tests/snippets/crystal/test_pseudo_keywords.txt
@@ -0,0 +1,50 @@
+---input---
+def f(x : T, line = __LINE__) forall T
+if x.is_a?(String)
+pp! x
+end
+end
+
+---tokens---
+'def' Keyword
+' ' Text.Whitespace
+'f' Name.Function
+'(' Punctuation
+'x' Name
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'T' Name
+',' Punctuation
+' ' Text.Whitespace
+'line' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'__LINE__' Keyword.Pseudo
+')' Punctuation
+' ' Text.Whitespace
+'forall' Keyword.Pseudo
+' ' Text.Whitespace
+'T' Name
+'\n' Text.Whitespace
+
+'if' Keyword
+' ' Text.Whitespace
+'x' Name
+'.is_a?' Keyword.Pseudo
+'(' Punctuation
+'String' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'pp!' Name.Builtin.Pseudo
+' ' Text.Whitespace
+'x' Name
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_range_syntax1.txt b/tests/snippets/crystal/test_range_syntax1.txt
new file mode 100644
index 0000000..a3ba24a
--- /dev/null
+++ b/tests/snippets/crystal/test_range_syntax1.txt
@@ -0,0 +1,8 @@
+---input---
+1...3
+
+---tokens---
+'1' Literal.Number.Integer
+'...' Operator
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/crystal/test_range_syntax2.txt b/tests/snippets/crystal/test_range_syntax2.txt
new file mode 100644
index 0000000..08bf4b1
--- /dev/null
+++ b/tests/snippets/crystal/test_range_syntax2.txt
@@ -0,0 +1,10 @@
+---input---
+1 .. 3
+
+---tokens---
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+'..' Operator
+' ' Text.Whitespace
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_braced_strings.txt b/tests/snippets/csound/test_braced_strings.txt
new file mode 100644
index 0000000..6921af9
--- /dev/null
+++ b/tests/snippets/csound/test_braced_strings.txt
@@ -0,0 +1,11 @@
+---input---
+{{
+characters$MACRO.
+}}
+
+---tokens---
+'{{' Literal.String
+'\ncharacters$MACRO.\n' Literal.String
+
+'}}' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_comments.txt b/tests/snippets/csound/test_comments.txt
new file mode 100644
index 0000000..e660c2b
--- /dev/null
+++ b/tests/snippets/csound/test_comments.txt
@@ -0,0 +1,16 @@
+---input---
+/*
+ * comment
+ */
+; comment
+// comment
+
+---tokens---
+'/*\n * comment\n */' Comment.Multiline
+'\n' Text.Whitespace
+
+'; comment' Comment.Single
+'\n' Text.Whitespace
+
+'// comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_escape_sequences.txt b/tests/snippets/csound/test_escape_sequences.txt
new file mode 100644
index 0000000..8e7ba10
--- /dev/null
+++ b/tests/snippets/csound/test_escape_sequences.txt
@@ -0,0 +1,122 @@
+---input---
+"\\"
+{{\\}}
+"\a"
+{{\a}}
+"\b"
+{{\b}}
+"\n"
+{{\n}}
+"\r"
+{{\r}}
+"\t"
+{{\t}}
+"\""
+{{\"}}
+"\012"
+{{\012}}
+"\345"
+{{\345}}
+"\67"
+{{\67}}
+
+---tokens---
+'"' Literal.String
+'\\\\' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\\\' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\a' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\a' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\b' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\b' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\n' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\n' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\r' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\r' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\t' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\t' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\"' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\"' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\012' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\012' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\345' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\345' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'\\67' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'{{' Literal.String
+'\\67' Literal.String.Escape
+'}}' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_function_like_macro_definitions.txt b/tests/snippets/csound/test_function_like_macro_definitions.txt
new file mode 100644
index 0000000..555f4ad
--- /dev/null
+++ b/tests/snippets/csound/test_function_like_macro_definitions.txt
@@ -0,0 +1,44 @@
+---input---
+#define MACRO(ARG1#ARG2) #macro_body#
+#define/**/
+MACRO(ARG1'ARG2' ARG3)/**/
+#\#macro
+body\##
+
+---tokens---
+'#define' Comment.Preproc
+' ' Text.Whitespace
+'MACRO' Comment.Preproc
+'(' Punctuation
+'ARG1' Comment.Preproc
+'#' Punctuation
+'ARG2' Comment.Preproc
+')' Punctuation
+' ' Text.Whitespace
+'#' Punctuation
+'macro_body' Comment.Preproc
+'#' Punctuation
+'\n' Text.Whitespace
+
+'#define' Comment.Preproc
+'/**/' Comment.Multiline
+'\n' Text.Whitespace
+
+'MACRO' Comment.Preproc
+'(' Punctuation
+'ARG1' Comment.Preproc
+"'" Punctuation
+'ARG2' Comment.Preproc
+"'" Punctuation
+' ' Text.Whitespace
+'ARG3' Comment.Preproc
+')' Punctuation
+'/**/' Comment.Multiline
+'\n' Text.Whitespace
+
+'#' Punctuation
+'\\#' Comment.Preproc
+'macro\nbody' Comment.Preproc
+'\\#' Comment.Preproc
+'#' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_function_like_macros.txt b/tests/snippets/csound/test_function_like_macros.txt
new file mode 100644
index 0000000..955cc18
--- /dev/null
+++ b/tests/snippets/csound/test_function_like_macros.txt
@@ -0,0 +1,40 @@
+---input---
+$MACRO.(((x#y\)))' "(#'x)\)x\))"# {{x\))x)\)(#'}});
+
+---tokens---
+'$MACRO.' Comment.Preproc
+'(' Punctuation
+'(' Comment.Preproc
+'(' Comment.Preproc
+'x#y\\)' Comment.Preproc
+')' Comment.Preproc
+')' Comment.Preproc
+"'" Punctuation
+' ' Comment.Preproc
+'"' Literal.String
+'(' Error
+'#' Error
+"'" Error
+'x' Literal.String
+')' Error
+'\\)' Comment.Preproc
+'x' Literal.String
+'\\)' Comment.Preproc
+')' Error
+'"' Literal.String
+'#' Punctuation
+' ' Comment.Preproc
+'{{' Literal.String
+'x' Literal.String
+'\\)' Comment.Preproc
+')' Error
+'x' Literal.String
+')' Error
+'\\)' Comment.Preproc
+'(' Error
+'#' Error
+"'" Error
+'}}' Literal.String
+')' Punctuation
+';' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_global_value_identifiers.txt b/tests/snippets/csound/test_global_value_identifiers.txt
new file mode 100644
index 0000000..4604200
--- /dev/null
+++ b/tests/snippets/csound/test_global_value_identifiers.txt
@@ -0,0 +1,30 @@
+---input---
+0dbfs
+A4
+kr
+ksmps
+nchnls
+nchnls_i
+sr
+
+---tokens---
+'0dbfs' Name.Variable.Global
+'\n' Text.Whitespace
+
+'A4' Name.Variable.Global
+'\n' Text.Whitespace
+
+'kr' Name.Variable.Global
+'\n' Text.Whitespace
+
+'ksmps' Name.Variable.Global
+'\n' Text.Whitespace
+
+'nchnls' Name.Variable.Global
+'\n' Text.Whitespace
+
+'nchnls_i' Name.Variable.Global
+'\n' Text.Whitespace
+
+'sr' Name.Variable.Global
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_goto_statements.txt b/tests/snippets/csound/test_goto_statements.txt
new file mode 100644
index 0000000..95a76ab
--- /dev/null
+++ b/tests/snippets/csound/test_goto_statements.txt
@@ -0,0 +1,176 @@
+---input---
+goto aLabel
+igoto aLabel
+kgoto aLabel
+reinit aLabel
+rigoto aLabel
+tigoto aLabel
+cggoto 1==0, aLabel
+cigoto 1==0, aLabel
+cingoto 1==0, aLabel
+ckgoto 1==0, aLabel
+cngoto 1==0, aLabel
+cnkgoto 1==0, aLabel
+timout 0, 0, aLabel
+loop_ge 0, 0, 0, aLabel
+loop_gt 0, 0, 0, aLabel
+loop_le 0, 0, 0, aLabel
+loop_lt 0, 0, 0, aLabel
+
+---tokens---
+'goto' Keyword
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'igoto' Keyword
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'kgoto' Keyword
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'reinit' Keyword.Pseudo
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'rigoto' Keyword.Pseudo
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'tigoto' Keyword.Pseudo
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'cggoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'cigoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'cingoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'ckgoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'cngoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'cnkgoto' Keyword.Pseudo
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'==' Operator
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'timout' Keyword.Pseudo
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'loop_ge' Keyword.Pseudo
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'loop_gt' Keyword.Pseudo
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'loop_le' Keyword.Pseudo
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
+
+'loop_lt' Keyword.Pseudo
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'aLabel' Name.Label
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_include_directives.txt b/tests/snippets/csound/test_include_directives.txt
new file mode 100644
index 0000000..b90f300
--- /dev/null
+++ b/tests/snippets/csound/test_include_directives.txt
@@ -0,0 +1,14 @@
+---input---
+#include/**/"file.udo"
+#include/**/|file.udo|
+
+---tokens---
+'#include' Comment.Preproc
+'/**/' Comment.Multiline
+'"file.udo"' Literal.String
+'\n' Text.Whitespace
+
+'#include' Comment.Preproc
+'/**/' Comment.Multiline
+'|file.udo|' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_includestr_directives.txt b/tests/snippets/csound/test_includestr_directives.txt
new file mode 100644
index 0000000..0511c5d
--- /dev/null
+++ b/tests/snippets/csound/test_includestr_directives.txt
@@ -0,0 +1,11 @@
+---input---
+#includestr/**/"$MACRO..udo"
+
+---tokens---
+'#includestr' Comment.Preproc
+'/**/' Comment.Multiline
+'"' Literal.String
+'$MACRO.' Comment.Preproc
+'.udo' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_instrument_blocks.txt b/tests/snippets/csound/test_instrument_blocks.txt
new file mode 100644
index 0000000..b6b0ba9
--- /dev/null
+++ b/tests/snippets/csound/test_instrument_blocks.txt
@@ -0,0 +1,42 @@
+---input---
+instr/**/1,/**/N_a_M_e_,/**/+Name/**///
+ iDuration = p3
+ outc:a(aSignal)
+endin
+
+---tokens---
+'instr' Keyword.Declaration
+'/**/' Comment.Multiline
+'1' Name.Function
+',' Punctuation
+'/**/' Comment.Multiline
+'N_a_M_e_' Name.Function
+',' Punctuation
+'/**/' Comment.Multiline
+'+' Punctuation
+'Name' Name.Function
+'/**/' Comment.Multiline
+'//' Comment.Single
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'i' Keyword.Type
+'Duration' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'p3' Name.Variable.Instance
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'outc' Name.Builtin
+':' Punctuation
+'a' Keyword.Type
+'(' Punctuation
+'a' Keyword.Type
+'Signal' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'endin' Keyword.Declaration
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_keywords.txt b/tests/snippets/csound/test_keywords.txt
new file mode 100644
index 0000000..eb67eca
--- /dev/null
+++ b/tests/snippets/csound/test_keywords.txt
@@ -0,0 +1,62 @@
+---input---
+do
+else
+elseif
+endif
+enduntil
+fi
+if
+ithen
+kthen
+od
+then
+until
+while
+return
+rireturn
+
+---tokens---
+'do' Keyword
+'\n' Text.Whitespace
+
+'else' Keyword
+'\n' Text.Whitespace
+
+'elseif' Keyword
+'\n' Text.Whitespace
+
+'endif' Keyword
+'\n' Text.Whitespace
+
+'enduntil' Keyword
+'\n' Text.Whitespace
+
+'fi' Keyword
+'\n' Text.Whitespace
+
+'if' Keyword
+'\n' Text.Whitespace
+
+'ithen' Keyword
+'\n' Text.Whitespace
+
+'kthen' Keyword
+'\n' Text.Whitespace
+
+'od' Keyword
+'\n' Text.Whitespace
+
+'then' Keyword
+'\n' Text.Whitespace
+
+'until' Keyword
+'\n' Text.Whitespace
+
+'while' Keyword
+'\n' Text.Whitespace
+
+'return' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'rireturn' Keyword.Pseudo
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_labels.txt b/tests/snippets/csound/test_labels.txt
new file mode 100644
index 0000000..afa6be9
--- /dev/null
+++ b/tests/snippets/csound/test_labels.txt
@@ -0,0 +1,13 @@
+---input---
+aLabel:
+ label2:
+
+---tokens---
+'aLabel' Name.Label
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'label2' Name.Label
+':' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_macro_preprocessor_directives.txt b/tests/snippets/csound/test_macro_preprocessor_directives.txt
new file mode 100644
index 0000000..0a576f6
--- /dev/null
+++ b/tests/snippets/csound/test_macro_preprocessor_directives.txt
@@ -0,0 +1,20 @@
+---input---
+#ifdef MACRO
+#ifndef MACRO
+#undef MACRO
+
+---tokens---
+'#ifdef' Comment.Preproc
+' ' Text.Whitespace
+'MACRO' Comment.Preproc
+'\n' Text.Whitespace
+
+'#ifndef' Comment.Preproc
+' ' Text.Whitespace
+'MACRO' Comment.Preproc
+'\n' Text.Whitespace
+
+'#undef' Comment.Preproc
+' ' Text.Whitespace
+'MACRO' Comment.Preproc
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_name.txt b/tests/snippets/csound/test_name.txt
new file mode 100644
index 0000000..7ae5d58
--- /dev/null
+++ b/tests/snippets/csound/test_name.txt
@@ -0,0 +1,9 @@
+---input---
+kG:V
+
+---tokens---
+'k' Keyword.Type
+'G' Name
+':' Punctuation
+'V' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_numbers.txt b/tests/snippets/csound/test_numbers.txt
new file mode 100644
index 0000000..63a8106
--- /dev/null
+++ b/tests/snippets/csound/test_numbers.txt
@@ -0,0 +1,52 @@
+---input---
+123 0123456789
+0xabcdef0123456789 0XABCDEF
+1e2
+3e+4
+5e-6
+7E8
+9E+0
+1E-2
+3.
+4.56
+.789
+
+---tokens---
+'123' Literal.Number.Integer
+' ' Text.Whitespace
+'0123456789' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'0x' Keyword.Type
+'abcdef0123456789' Literal.Number.Hex
+' ' Text.Whitespace
+'0X' Keyword.Type
+'ABCDEF' Literal.Number.Hex
+'\n' Text.Whitespace
+
+'1e2' Literal.Number.Float
+'\n' Text.Whitespace
+
+'3e+4' Literal.Number.Float
+'\n' Text.Whitespace
+
+'5e-6' Literal.Number.Float
+'\n' Text.Whitespace
+
+'7E8' Literal.Number.Float
+'\n' Text.Whitespace
+
+'9E+0' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1E-2' Literal.Number.Float
+'\n' Text.Whitespace
+
+'3.' Literal.Number.Float
+'\n' Text.Whitespace
+
+'4.56' Literal.Number.Float
+'\n' Text.Whitespace
+
+'.789' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_object_like_macro_definitions.txt b/tests/snippets/csound/test_object_like_macro_definitions.txt
new file mode 100644
index 0000000..dd165f3
--- /dev/null
+++ b/tests/snippets/csound/test_object_like_macro_definitions.txt
@@ -0,0 +1,30 @@
+---input---
+# define MACRO#macro_body#
+#define/**/
+MACRO/**/
+#\#macro
+body\##
+
+---tokens---
+'# \tdefine' Comment.Preproc
+' ' Text.Whitespace
+'MACRO' Comment.Preproc
+'#' Punctuation
+'macro_body' Comment.Preproc
+'#' Punctuation
+'\n' Text.Whitespace
+
+'#define' Comment.Preproc
+'/**/' Comment.Multiline
+'\n' Text.Whitespace
+
+'MACRO' Comment.Preproc
+'/**/' Comment.Multiline
+'\n' Text.Whitespace
+
+'#' Punctuation
+'\\#' Comment.Preproc
+'macro\nbody' Comment.Preproc
+'\\#' Comment.Preproc
+'#' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_operators.txt b/tests/snippets/csound/test_operators.txt
new file mode 100644
index 0000000..1154a44
--- /dev/null
+++ b/tests/snippets/csound/test_operators.txt
@@ -0,0 +1,114 @@
+---input---
++
+-
+~
+!
+*
+/
+^
+%
+<<
+>>
+<
+>
+<=
+>=
+==
+!=
+&
+#
+|
+&&
+||
+?
+:
++=
+-=
+*=
+/=
+
+---tokens---
+'+' Operator
+'\n' Text.Whitespace
+
+'-' Operator
+'\n' Text.Whitespace
+
+'~' Operator
+'\n' Text.Whitespace
+
+'¬' Operator
+'\n' Text.Whitespace
+
+'!' Operator
+'\n' Text.Whitespace
+
+'*' Operator
+'\n' Text.Whitespace
+
+'/' Operator
+'\n' Text.Whitespace
+
+'^' Operator
+'\n' Text.Whitespace
+
+'%' Operator
+'\n' Text.Whitespace
+
+'<<' Operator
+'\n' Text.Whitespace
+
+'>>' Operator
+'\n' Text.Whitespace
+
+'<' Operator
+'\n' Text.Whitespace
+
+'>' Operator
+'\n' Text.Whitespace
+
+'<=' Operator
+'\n' Text.Whitespace
+
+'>=' Operator
+'\n' Text.Whitespace
+
+'==' Operator
+'\n' Text.Whitespace
+
+'!=' Operator
+'\n' Text.Whitespace
+
+'&' Operator
+'\n' Text.Whitespace
+
+'#' Operator
+'\n' Text.Whitespace
+
+'|' Operator
+'\n' Text.Whitespace
+
+'&&' Operator
+'\n' Text.Whitespace
+
+'||' Operator
+'\n' Text.Whitespace
+
+'?' Operator
+'\n' Text.Whitespace
+
+':' Operator
+'\n' Text.Whitespace
+
+'+=' Operator
+'\n' Text.Whitespace
+
+'-=' Operator
+'\n' Text.Whitespace
+
+'*=' Operator
+'\n' Text.Whitespace
+
+'/=' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_other_preprocessor_directives.txt b/tests/snippets/csound/test_other_preprocessor_directives.txt
new file mode 100644
index 0000000..95026a0
--- /dev/null
+++ b/tests/snippets/csound/test_other_preprocessor_directives.txt
@@ -0,0 +1,26 @@
+---input---
+#else
+#end
+#endif
+###
+@ 12345
+@@ 67890
+
+---tokens---
+'#else' Comment.Preproc
+'\n' Text.Whitespace
+
+'#end' Comment.Preproc
+'\n' Text.Whitespace
+
+'#endif' Comment.Preproc
+'\n' Text.Whitespace
+
+'###' Comment.Preproc
+'\n' Text.Whitespace
+
+'@ \t12345' Comment.Preproc
+'\n' Text.Whitespace
+
+'@@ \t67890' Comment.Preproc
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_printks_and_prints_escape_sequences.txt b/tests/snippets/csound/test_printks_and_prints_escape_sequences.txt
new file mode 100644
index 0000000..3663dbb
--- /dev/null
+++ b/tests/snippets/csound/test_printks_and_prints_escape_sequences.txt
@@ -0,0 +1,290 @@
+---input---
+printks "%!"
+printks "%%"
+printks "%n"
+printks "%N"
+printks "%r"
+printks "%R"
+printks "%t"
+printks "%T"
+printks "\\a"
+printks "\\A"
+printks "\\b"
+printks "\\B"
+printks "\\n"
+printks "\\N"
+printks "\\r"
+printks "\\R"
+printks "\\t"
+printks "\\T"
+prints "%!"
+prints "%%"
+prints "%n"
+prints "%N"
+prints "%r"
+prints "%R"
+prints "%t"
+prints "%T"
+prints "\\a"
+prints "\\A"
+prints "\\b"
+prints "\\B"
+prints "\\n"
+prints "\\N"
+prints "\\r"
+prints "\\R"
+prints "\\t"
+prints "\\T"
+
+---tokens---
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%!' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%%' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%n' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%N' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%r' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%R' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%t' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%T' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\a' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\A' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\b' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\B' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\n' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\N' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\r' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\R' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\t' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'printks' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\T' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%!' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%%' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%n' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%N' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%r' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%R' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%t' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'%T' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\a' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\A' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\b' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\B' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\n' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\N' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\r' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\R' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\t' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'prints' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String
+'\\\\T' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_quoted_strings.txt b/tests/snippets/csound/test_quoted_strings.txt
new file mode 100644
index 0000000..d7e828a
--- /dev/null
+++ b/tests/snippets/csound/test_quoted_strings.txt
@@ -0,0 +1,9 @@
+---input---
+"characters$MACRO."
+
+---tokens---
+'"' Literal.String
+'characters' Literal.String
+'$MACRO.' Comment.Preproc
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/csound/test_user_defined_opcodes.txt b/tests/snippets/csound/test_user_defined_opcodes.txt
new file mode 100644
index 0000000..6320566
--- /dev/null
+++ b/tests/snippets/csound/test_user_defined_opcodes.txt
@@ -0,0 +1,24 @@
+---input---
+opcode/**/aUDO,/**/i[],/**/aik//
+ aUDO
+endop
+
+---tokens---
+'opcode' Keyword.Declaration
+'/**/' Comment.Multiline
+'aUDO' Name.Function
+',' Punctuation
+'/**/' Comment.Multiline
+'i[]' Keyword.Type
+',' Punctuation
+'/**/' Comment.Multiline
+'aik' Keyword.Type
+'//' Comment.Single
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'aUDO' Name.Function
+'\n' Text.Whitespace
+
+'endop' Keyword.Declaration
+'\n' Text.Whitespace
diff --git a/tests/snippets/doscon/test_gt_only.txt b/tests/snippets/doscon/test_gt_only.txt
new file mode 100644
index 0000000..b37b8fa
--- /dev/null
+++ b/tests/snippets/doscon/test_gt_only.txt
@@ -0,0 +1,11 @@
+---input---
+> py
+hi
+
+---tokens---
+'>' Generic.Prompt
+' ' Text
+'py' Text
+'\n' Text
+
+'hi\n' Generic.Output
diff --git a/tests/snippets/elpi/test_catastrophic_backtracking.txt b/tests/snippets/elpi/test_catastrophic_backtracking.txt
new file mode 100644
index 0000000..a14a054
--- /dev/null
+++ b/tests/snippets/elpi/test_catastrophic_backtracking.txt
@@ -0,0 +1,6 @@
+---input---
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+---tokens---
+'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/elpi/test_chr.txt b/tests/snippets/elpi/test_chr.txt
new file mode 100644
index 0000000..75291a3
--- /dev/null
+++ b/tests/snippets/elpi/test_chr.txt
@@ -0,0 +1,54 @@
+---input---
+constraint foo, bar {
+
+ :name "myrule"
+ rule (odd X) \ (even X) | true <=> fail.
+
+}
+rule. % not a kwd
+
+---tokens---
+'constraint' Keyword.Declaration
+' ' Text.Whitespace
+'foo, bar ' Name.Function
+'{' Text
+'\n\n ' Text.Whitespace
+':name' Keyword.Mode
+' ' Text.Whitespace
+'"' Literal.String.Double
+'myrule' Literal.String.Double
+'"' Literal.String.Double
+'\n ' Text.Whitespace
+'rule' Keyword.Declaration
+' ' Text.Whitespace
+'(' Text
+'odd' Text
+' ' Text.Whitespace
+'X' Name.Variable
+')' Operator
+' ' Text.Whitespace
+'\\' Keyword.Declaration
+' ' Text.Whitespace
+'(' Text
+'even' Text
+' ' Text.Whitespace
+'X' Name.Variable
+')' Operator
+' ' Text.Whitespace
+'|' Keyword.Declaration
+' ' Text.Whitespace
+'true' Text
+' ' Text.Whitespace
+'<=>' Keyword.Declaration
+' ' Text.Whitespace
+'fail' Text
+'.' Operator
+'\n\n' Text.Whitespace
+
+'}' Text
+'\n' Text.Whitespace
+
+'rule' Text
+'.' Operator
+' ' Text.Whitespace
+'% not a kwd\n' Comment
diff --git a/tests/snippets/elpi/test_clause.txt b/tests/snippets/elpi/test_clause.txt
new file mode 100644
index 0000000..e485753
--- /dev/null
+++ b/tests/snippets/elpi/test_clause.txt
@@ -0,0 +1,67 @@
+---input---
+true.
+stop :- !.
+of (fun F) :- pi x\ of x => of (F x).
+match (uvar as Y) :- print Y.
+
+---tokens---
+'true' Text
+'.' Operator
+'\n' Text.Whitespace
+
+'stop' Text
+' ' Text.Whitespace
+':-' Keyword.Declaration
+' ' Text.Whitespace
+'!' Keyword.Declaration
+'.' Operator
+'\n' Text.Whitespace
+
+'of' Text
+' ' Text.Whitespace
+'(' Text
+'fun' Text
+' ' Text.Whitespace
+'F' Name.Variable
+')' Operator
+' ' Text.Whitespace
+':-' Keyword.Declaration
+' ' Text.Whitespace
+'pi' Keyword.Declaration
+' ' Text.Whitespace
+'x' Name.Variable
+'\\' Text
+' ' Text.Whitespace
+'of' Text
+' ' Text.Whitespace
+'x' Text
+' ' Text.Whitespace
+'=>' Keyword.Declaration
+' ' Text.Whitespace
+'of' Text
+' ' Text.Whitespace
+'(' Text
+'F' Name.Variable
+' ' Text.Whitespace
+'x' Text
+')' Operator
+'.' Operator
+'\n' Text.Whitespace
+
+'match' Text
+' ' Text.Whitespace
+'(' Text
+'uvar' Keyword.Declaration
+' ' Text.Whitespace
+'as' Keyword.Declaration
+' ' Text.Whitespace
+'Y' Name.Variable
+')' Operator
+' ' Text.Whitespace
+':-' Keyword.Declaration
+' ' Text.Whitespace
+'print' Text
+' ' Text.Whitespace
+'Y' Name.Variable
+'.' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/elpi/test_namespace.txt b/tests/snippets/elpi/test_namespace.txt
new file mode 100644
index 0000000..98a35b0
--- /dev/null
+++ b/tests/snippets/elpi/test_namespace.txt
@@ -0,0 +1,35 @@
+---input---
+namespace foo.bar {
+ baz :- std.do! [].
+}
+shorten foo. { bar }.
+
+---tokens---
+'namespace' Keyword.Declaration
+' ' Text.Whitespace
+'foo.bar' Text
+' ' Text.Whitespace
+'{\n' Text
+
+' ' Text.Whitespace
+'baz' Text
+' ' Text.Whitespace
+':-' Keyword.Declaration
+' ' Text.Whitespace
+'std.do!' Text
+' ' Text.Whitespace
+'[]' Keyword.Declaration
+'.' Operator
+'\n' Text.Whitespace
+
+'}\n' Text
+
+'shorten' Keyword.Declaration
+' ' Text.Whitespace
+'foo.' Text
+' ' Text.Whitespace
+'{ ' Text
+'bar' Text
+' ' Text.Whitespace
+'}.' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/elpi/test_pred.txt b/tests/snippets/elpi/test_pred.txt
new file mode 100644
index 0000000..657c8fd
--- /dev/null
+++ b/tests/snippets/elpi/test_pred.txt
@@ -0,0 +1,60 @@
+---input---
+pred p1.
+pred p2 i:int, o:list A.
+pred p3 i:(bool -> prop).
+:index(_ 2) pred p4 i:int, i:A.
+
+---tokens---
+'pred' Keyword.Declaration
+' ' Text.Whitespace
+'p1' Name.Function
+'.' Text
+'\n' Text.Whitespace
+
+'pred' Keyword.Declaration
+' ' Text.Whitespace
+'p2' Name.Function
+' ' Text.Whitespace
+'i:' Keyword.Mode
+'int' Keyword.Type
+',' Text
+' ' Text.Whitespace
+'o:' Keyword.Mode
+'list' Keyword.Type
+' ' Text.Whitespace
+'A' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'pred' Keyword.Declaration
+' ' Text.Whitespace
+'p3' Name.Function
+' ' Text.Whitespace
+'i:' Keyword.Mode
+'(' Keyword.Type
+'bool' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'prop' Keyword.Type
+')' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+':index' Keyword.Mode
+'(' Text.Whitespace
+'_ 2' Literal.Number.Integer
+')' Text
+' ' Text.Whitespace
+'pred' Keyword.Declaration
+' ' Text.Whitespace
+'p4' Name.Function
+' ' Text.Whitespace
+'i:' Keyword.Mode
+'int' Keyword.Type
+',' Text
+' ' Text.Whitespace
+'i:' Keyword.Mode
+'A' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/elpi/test_type.txt b/tests/snippets/elpi/test_type.txt
new file mode 100644
index 0000000..8a506ce
--- /dev/null
+++ b/tests/snippets/elpi/test_type.txt
@@ -0,0 +1,112 @@
+---input---
+kind list type -> type.
+type nil list A.
+type cons A -> list A -> list A.
+kind tm type.
+type fun (tm -> tm) -> tm.
+type app tm -> tm -> tm.
+pred foo i:(tm -> tm), o:tm.
+
+---tokens---
+'kind' Keyword.Declaration
+' ' Text.Whitespace
+'list' Name.Function
+' ' Text.Whitespace
+'type' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'type' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'type' Keyword.Declaration
+' ' Text.Whitespace
+'nil' Name.Function
+' ' Text.Whitespace
+'list' Keyword.Type
+' ' Text.Whitespace
+'A' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'type' Keyword.Declaration
+' ' Text.Whitespace
+'cons' Name.Function
+' ' Text.Whitespace
+'A' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'list' Keyword.Type
+' ' Text.Whitespace
+'A' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'list' Keyword.Type
+' ' Text.Whitespace
+'A' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'kind' Keyword.Declaration
+' ' Text.Whitespace
+'tm' Name.Function
+' ' Text.Whitespace
+'type' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'type' Keyword.Declaration
+' ' Text.Whitespace
+'fun' Name.Function
+' ' Text.Whitespace
+'(' Keyword.Type
+'tm' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'tm' Keyword.Type
+')' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'tm' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'type' Keyword.Declaration
+' ' Text.Whitespace
+'app' Name.Function
+' ' Text.Whitespace
+'tm' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'tm' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'tm' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
+
+'pred' Keyword.Declaration
+' ' Text.Whitespace
+'foo' Name.Function
+' ' Text.Whitespace
+'i:' Keyword.Mode
+'(' Keyword.Type
+'tm' Keyword.Type
+' ' Text.Whitespace
+'->' Keyword.Type
+' ' Text.Whitespace
+'tm' Keyword.Type
+')' Keyword.Type
+',' Text
+' ' Text.Whitespace
+'o:' Keyword.Mode
+'tm' Keyword.Type
+'.' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/ezhil/test_function.txt b/tests/snippets/ezhil/test_function.txt
new file mode 100644
index 0000000..f89a357
--- /dev/null
+++ b/tests/snippets/ezhil/test_function.txt
@@ -0,0 +1,100 @@
+---input---
+# (C) முத்தையா அண்ணாமலை 2013, 2015
+நிரல்பாகம் gcd ( x, y )
+மு = max(x,y)
+ q = min(x,y)
+
+@( q == 0 ) ஆனால்
+ பின்கொடு மு
+முடி
+பின்கொடு gcd( மு - q , q )
+முடி
+
+---tokens---
+'# (C) முத்தையா அண்ணாமலை 2013, 2015' Comment.Single
+'\n' Text.Whitespace
+
+'நிரல்பாகம்' Keyword
+' ' Text.Whitespace
+'gcd' Name
+' ' Text.Whitespace
+'(' Punctuation
+' ' Text.Whitespace
+'x' Name
+',' Operator
+' ' Text.Whitespace
+'y' Name
+' ' Text.Whitespace
+')' Punctuation
+'\n' Text.Whitespace
+
+'மு' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'max' Name.Builtin
+'(' Punctuation
+'x' Name
+',' Operator
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'q' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'min' Name.Builtin
+'(' Punctuation
+'x' Name
+',' Operator
+'y' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'@' Operator
+'(' Punctuation
+' ' Text.Whitespace
+'q' Name
+' ' Text.Whitespace
+'==' Operator
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+' ' Text.Whitespace
+')' Punctuation
+' ' Text.Whitespace
+'ஆனால்' Keyword
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'பின்கொடு' Keyword
+' ' Text.Whitespace
+'மு' Name
+'\n' Text.Whitespace
+
+'முடி' Keyword
+'\n' Text.Whitespace
+
+'பின்கொடு' Keyword
+' ' Text.Whitespace
+'gcd' Name
+'(' Punctuation
+' ' Text.Whitespace
+'மு' Name
+' ' Text.Whitespace
+'-' Operator
+' ' Text.Whitespace
+'q' Name
+' ' Text.Whitespace
+',' Operator
+' ' Text.Whitespace
+'q' Name
+' ' Text.Whitespace
+')' Punctuation
+'\n' Text.Whitespace
+
+'முடி' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/ezhil/test_gcd_expr.txt b/tests/snippets/ezhil/test_gcd_expr.txt
new file mode 100644
index 0000000..f4aaed9
--- /dev/null
+++ b/tests/snippets/ezhil/test_gcd_expr.txt
@@ -0,0 +1,21 @@
+---input---
+1^3+(5-5)*gcd(a,b)
+
+---tokens---
+'1' Literal.Number.Integer
+'^' Operator
+'3' Literal.Number.Integer
+'+' Operator
+'(' Punctuation
+'5' Literal.Number.Integer
+'-' Operator
+'5' Literal.Number.Integer
+')' Punctuation
+'*' Operator
+'gcd' Name
+'(' Punctuation
+'a' Name
+',' Operator
+'b' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/ezhil/test_if_statement.txt b/tests/snippets/ezhil/test_if_statement.txt
new file mode 100644
index 0000000..2fe35b7
--- /dev/null
+++ b/tests/snippets/ezhil/test_if_statement.txt
@@ -0,0 +1,28 @@
+---input---
+@( 0 > 3 ) ஆனால்
+ பதிப்பி "wont print"
+முடி
+
+---tokens---
+'@' Operator
+'(' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+' ' Text.Whitespace
+'>' Operator
+' ' Text.Whitespace
+'3' Literal.Number.Integer
+' ' Text.Whitespace
+')' Punctuation
+' ' Text.Whitespace
+'ஆனால்' Keyword
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'பதிப்பி' Keyword
+' ' Text.Whitespace
+'"wont print"' Literal.String
+'\n' Text.Whitespace
+
+'முடி' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/ezhil/test_sum.txt b/tests/snippets/ezhil/test_sum.txt
new file mode 100644
index 0000000..fa2063d
--- /dev/null
+++ b/tests/snippets/ezhil/test_sum.txt
@@ -0,0 +1,8 @@
+---input---
+1+3
+
+---tokens---
+'1' Literal.Number.Integer
+'+' Operator
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/fortran/test_string_cataback.txt b/tests/snippets/fortran/test_string_cataback.txt
new file mode 100644
index 0000000..9f4b9f1
--- /dev/null
+++ b/tests/snippets/fortran/test_string_cataback.txt
@@ -0,0 +1,112 @@
+---input---
+! Bad string, there isn't an even number of backslashes.
+! This should not cause catastrophic backtracking.
+'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+
+---tokens---
+"! Bad string, there isn't an even number of backslashes.\n" Comment
+
+'! This should not cause catastrophic backtracking.\n' Comment
+
+"'" Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+'\\' Error
+"'" Error
+'\n' Text.Whitespace
diff --git a/tests/snippets/gas/test_comments.txt b/tests/snippets/gas/test_comments.txt
new file mode 100644
index 0000000..b385d27
--- /dev/null
+++ b/tests/snippets/gas/test_comments.txt
@@ -0,0 +1,29 @@
+---input---
+lock addq $0, /* comments */ (%rsp) /*
+// comments
+*/ xorq %rax, %rax // comments
+
+---tokens---
+'lock' Name.Attribute
+' ' Text.Whitespace
+'addq' Name.Function
+' ' Text.Whitespace
+'$0' Name.Constant
+',' Punctuation
+' ' Text.Whitespace
+'/* comments */' Comment.Multiline
+' ' Text.Whitespace
+'(' Punctuation
+'%rsp' Name.Variable
+')' Punctuation
+' ' Text.Whitespace
+'/*\n// comments\n*/' Comment.Multiline
+' ' Text.Whitespace
+'xorq' Name.Function
+' ' Text.Whitespace
+'%rax' Name.Variable
+',' Punctuation
+' ' Text.Whitespace
+'%rax' Name.Variable
+' ' Text.Whitespace
+'// comments\n' Comment.Single
diff --git a/tests/snippets/gdscript/test_comment.txt b/tests/snippets/gdscript/test_comment.txt
new file mode 100644
index 0000000..ee78bc5
--- /dev/null
+++ b/tests/snippets/gdscript/test_comment.txt
@@ -0,0 +1,6 @@
+---input---
+# Comment
+
+---tokens---
+'# Comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_export_array.txt b/tests/snippets/gdscript/test_export_array.txt
new file mode 100644
index 0000000..24a313a
--- /dev/null
+++ b/tests/snippets/gdscript/test_export_array.txt
@@ -0,0 +1,17 @@
+---input---
+export (Array, AudioStream) var streams
+
+---tokens---
+'export' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'Array' Name.Builtin.Type
+',' Punctuation
+' ' Text.Whitespace
+'AudioStream' Name
+')' Punctuation
+' ' Text.Whitespace
+'var' Keyword
+' ' Text.Whitespace
+'streams' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_function_with_types.txt b/tests/snippets/gdscript/test_function_with_types.txt
new file mode 100644
index 0000000..eedf371
--- /dev/null
+++ b/tests/snippets/gdscript/test_function_with_types.txt
@@ -0,0 +1,33 @@
+---input---
+func abc(arg: String) -> void:
+ print("Hello", arg)
+
+---tokens---
+'func' Keyword
+' ' Text.Whitespace
+'abc' Name
+'(' Punctuation
+'arg' Name
+':' Punctuation
+' ' Text.Whitespace
+'String' Name.Builtin.Type
+')' Punctuation
+' ' Text.Whitespace
+'-' Operator
+'>' Operator
+' ' Text.Whitespace
+'void' Name.Builtin.Type
+':' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'print' Name.Builtin
+'(' Punctuation
+'"' Literal.String.Double
+'Hello' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+' ' Text.Whitespace
+'arg' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_inner_class.txt b/tests/snippets/gdscript/test_inner_class.txt
new file mode 100644
index 0000000..734242b
--- /dev/null
+++ b/tests/snippets/gdscript/test_inner_class.txt
@@ -0,0 +1,20 @@
+---input---
+class InnerClass:
+ var a = 5
+
+---tokens---
+'class' Keyword
+' ' Text.Whitespace
+'InnerClass' Name
+':' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'var' Keyword
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'5' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_multiline_string.txt b/tests/snippets/gdscript/test_multiline_string.txt
new file mode 100644
index 0000000..b098207
--- /dev/null
+++ b/tests/snippets/gdscript/test_multiline_string.txt
@@ -0,0 +1,8 @@
+---input---
+"""
+Multiline
+"""
+
+---tokens---
+'"""\nMultiline\n"""' Literal.String.Doc
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_signal.txt b/tests/snippets/gdscript/test_signal.txt
new file mode 100644
index 0000000..43aa8ec
--- /dev/null
+++ b/tests/snippets/gdscript/test_signal.txt
@@ -0,0 +1,15 @@
+---input---
+signal sig (arg1, arg2)
+
+---tokens---
+'signal' Keyword
+' ' Text.Whitespace
+'sig' Name
+' ' Text.Whitespace
+'(' Punctuation
+'arg1' Name
+',' Punctuation
+' ' Text.Whitespace
+'arg2' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_simple_function.txt b/tests/snippets/gdscript/test_simple_function.txt
new file mode 100644
index 0000000..2f444ef
--- /dev/null
+++ b/tests/snippets/gdscript/test_simple_function.txt
@@ -0,0 +1,22 @@
+---input---
+func abc(arg):
+ print("Hello, World!")
+
+---tokens---
+'func' Keyword
+' ' Text.Whitespace
+'abc' Name
+'(' Punctuation
+'arg' Name
+')' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'print' Name.Builtin
+'(' Punctuation
+'"' Literal.String.Double
+'Hello, World!' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/gdscript/test_variable_declaration_and_assigment.txt b/tests/snippets/gdscript/test_variable_declaration_and_assigment.txt
new file mode 100644
index 0000000..b2ee890
--- /dev/null
+++ b/tests/snippets/gdscript/test_variable_declaration_and_assigment.txt
@@ -0,0 +1,12 @@
+---input---
+var abc = 5.4
+
+---tokens---
+'var' Keyword
+' ' Text.Whitespace
+'abc' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'5.4' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/haskell/test_promoted_names.txt b/tests/snippets/haskell/test_promoted_names.txt
new file mode 100644
index 0000000..42d7755
--- /dev/null
+++ b/tests/snippets/haskell/test_promoted_names.txt
@@ -0,0 +1,10 @@
+---input---
+'x ': '[]
+
+---tokens---
+"'x" Name
+' ' Text.Whitespace
+"':" Keyword.Type
+' ' Text.Whitespace
+"'[]" Keyword.Type
+'\n' Text.Whitespace
diff --git a/tests/snippets/html/multiline-comment-catastrophic-backtracking.txt b/tests/snippets/html/multiline-comment-catastrophic-backtracking.txt
new file mode 100644
index 0000000..76f2fb9
--- /dev/null
+++ b/tests/snippets/html/multiline-comment-catastrophic-backtracking.txt
@@ -0,0 +1,34 @@
+---input---
+<!--
+this
+comment
+is
+never
+terminated
+...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+...
+
+---tokens---
+'<' Error
+'!--\nthis\ncomment\nis\nnever\nterminated\n...\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n...\n' Text
diff --git a/tests/snippets/http/test_application_calendar_xml.txt b/tests/snippets/http/test_application_calendar_xml.txt
new file mode 100644
index 0000000..beb6386
--- /dev/null
+++ b/tests/snippets/http/test_application_calendar_xml.txt
@@ -0,0 +1,28 @@
+---input---
+GET / HTTP/1.0
+Content-Type: application/calendar+xml
+
+<foo>
+
+---tokens---
+'GET' Name.Function
+' ' Text
+'/' Name.Namespace
+' ' Text
+'HTTP' Keyword.Reserved
+'/' Operator
+'1.0' Literal.Number
+'\n' Text
+
+'Content-Type' Name.Attribute
+'' Text
+':' Operator
+' ' Text
+'application/calendar+xml' Literal
+'\n' Text
+
+'\n' Text
+
+'<foo' Name.Tag
+'>' Name.Tag
+'\n' Text.Whitespace
diff --git a/tests/snippets/http/test_application_xml.txt b/tests/snippets/http/test_application_xml.txt
new file mode 100644
index 0000000..97b2943
--- /dev/null
+++ b/tests/snippets/http/test_application_xml.txt
@@ -0,0 +1,28 @@
+---input---
+GET / HTTP/1.0
+Content-Type: application/xml
+
+<foo>
+
+---tokens---
+'GET' Name.Function
+' ' Text
+'/' Name.Namespace
+' ' Text
+'HTTP' Keyword.Reserved
+'/' Operator
+'1.0' Literal.Number
+'\n' Text
+
+'Content-Type' Name.Attribute
+'' Text
+':' Operator
+' ' Text
+'application/xml' Literal
+'\n' Text
+
+'\n' Text
+
+'<foo' Name.Tag
+'>' Name.Tag
+'\n' Text.Whitespace
diff --git a/tests/snippets/http/test_http_status_line.txt b/tests/snippets/http/test_http_status_line.txt
new file mode 100644
index 0000000..8f8449d
--- /dev/null
+++ b/tests/snippets/http/test_http_status_line.txt
@@ -0,0 +1,12 @@
+---input---
+HTTP/1.1 200 OK
+
+---tokens---
+'HTTP' Keyword.Reserved
+'/' Operator
+'1.1' Literal.Number
+' ' Text
+'200' Literal.Number
+' ' Text
+'OK' Name.Exception
+'\n' Text
diff --git a/tests/snippets/http/test_http_status_line_without_reason_phrase.txt b/tests/snippets/http/test_http_status_line_without_reason_phrase.txt
new file mode 100644
index 0000000..91bfa0e
--- /dev/null
+++ b/tests/snippets/http/test_http_status_line_without_reason_phrase.txt
@@ -0,0 +1,10 @@
+---input---
+HTTP/1.1 200
+
+---tokens---
+'HTTP' Keyword.Reserved
+'/' Operator
+'1.1' Literal.Number
+' ' Text
+'200' Literal.Number
+'\n' Text
diff --git a/tests/snippets/http/test_http_status_line_without_reason_phrase_rfc_7230.txt b/tests/snippets/http/test_http_status_line_without_reason_phrase_rfc_7230.txt
new file mode 100644
index 0000000..e0c9896
--- /dev/null
+++ b/tests/snippets/http/test_http_status_line_without_reason_phrase_rfc_7230.txt
@@ -0,0 +1,11 @@
+---input---
+HTTP/1.1 200
+
+---tokens---
+'HTTP' Keyword.Reserved
+'/' Operator
+'1.1' Literal.Number
+' ' Text
+'200' Literal.Number
+' ' Text
+'\n' Text
diff --git a/tests/snippets/idris/test_compiler_directive.txt b/tests/snippets/idris/test_compiler_directive.txt
new file mode 100644
index 0000000..68e18cb
--- /dev/null
+++ b/tests/snippets/idris/test_compiler_directive.txt
@@ -0,0 +1,20 @@
+---input---
+%link C "object.o"
+%name Vect xs
+
+---tokens---
+'%link' Keyword.Reserved
+' ' Text.Whitespace
+'C' Keyword.Type
+' ' Text.Whitespace
+'"' Literal.String
+'object.o' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'%name' Keyword.Reserved
+' ' Text.Whitespace
+'Vect' Keyword.Type
+' ' Text.Whitespace
+'xs' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/idris/test_reserved_word.txt b/tests/snippets/idris/test_reserved_word.txt
new file mode 100644
index 0000000..636bc16
--- /dev/null
+++ b/tests/snippets/idris/test_reserved_word.txt
@@ -0,0 +1,29 @@
+---input---
+namespace Foobar
+ links : String
+ links = "abc"
+
+---tokens---
+'namespace' Keyword.Reserved
+' ' Text.Whitespace
+'Foobar' Keyword.Type
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'links' Name.Function
+' ' Text.Whitespace
+':' Operator.Word
+' ' Text.Whitespace
+'String' Keyword.Type
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+' ' Text.Whitespace
+'links' Text
+' ' Text.Whitespace
+'=' Operator.Word
+' ' Text.Whitespace
+'"' Literal.String
+'abc' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/ini/test_indented_entries_1.txt b/tests/snippets/ini/test_indented_entries_1.txt
new file mode 100644
index 0000000..a878176
--- /dev/null
+++ b/tests/snippets/ini/test_indented_entries_1.txt
@@ -0,0 +1,16 @@
+---input---
+[section]
+ key1=value1
+ key2=value2
+
+---tokens---
+'[section]' Keyword
+'\n ' Text.Whitespace
+'key1' Name.Attribute
+'=' Operator
+'value1' Literal.String
+'\n ' Text.Whitespace
+'key2' Name.Attribute
+'=' Operator
+'value2' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/ini/test_indented_entries_2.txt b/tests/snippets/ini/test_indented_entries_2.txt
new file mode 100644
index 0000000..bd7c882
--- /dev/null
+++ b/tests/snippets/ini/test_indented_entries_2.txt
@@ -0,0 +1,20 @@
+---input---
+[section]
+ key1 = value1
+ key2 = value2
+
+---tokens---
+'[section]' Keyword
+'\n ' Text.Whitespace
+'key1' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'value1' Literal.String
+'\n ' Text.Whitespace
+'key2' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'value2' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/ini/test_indented_entries_3.txt b/tests/snippets/ini/test_indented_entries_3.txt
new file mode 100644
index 0000000..4a228af
--- /dev/null
+++ b/tests/snippets/ini/test_indented_entries_3.txt
@@ -0,0 +1,20 @@
+---input---
+[section]
+ key 1 = value1
+ key 2 = value2
+
+---tokens---
+'[section]' Keyword
+'\n ' Text.Whitespace
+'key 1' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'value1' Literal.String
+'\n ' Text.Whitespace
+'key 2' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'value2' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/j/test_deal_operator.txt b/tests/snippets/j/test_deal_operator.txt
new file mode 100644
index 0000000..0156b6d
--- /dev/null
+++ b/tests/snippets/j/test_deal_operator.txt
@@ -0,0 +1,8 @@
+---input---
+3?10
+
+---tokens---
+'3' Literal.Number.Integer
+'?' Operator
+'10' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/j/test_deal_operator_fixed_seed.txt b/tests/snippets/j/test_deal_operator_fixed_seed.txt
new file mode 100644
index 0000000..0a0bd77
--- /dev/null
+++ b/tests/snippets/j/test_deal_operator_fixed_seed.txt
@@ -0,0 +1,9 @@
+---input---
+3?.10
+
+---tokens---
+'3' Literal.Number.Integer
+'?' Operator
+'.' Operator
+'10' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_default.txt b/tests/snippets/java/test_default.txt
new file mode 100644
index 0000000..f24fa42
--- /dev/null
+++ b/tests/snippets/java/test_default.txt
@@ -0,0 +1,36 @@
+---input---
+switch (x) {
+ case 1: break;
+ default: break;
+}
+
+---tokens---
+'switch' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'x' Name
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'case' Keyword
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+':' Punctuation
+' ' Text.Whitespace
+'break' Keyword
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'default' Keyword
+':' Punctuation
+' ' Text.Whitespace
+'break' Keyword
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_enhanced_for.txt b/tests/snippets/java/test_enhanced_for.txt
new file mode 100644
index 0000000..d2a8091
--- /dev/null
+++ b/tests/snippets/java/test_enhanced_for.txt
@@ -0,0 +1,22 @@
+---input---
+label:
+for(String var2: var1) {}
+
+---tokens---
+'label' Name.Label
+':' Punctuation
+'\n' Text.Whitespace
+
+'for' Keyword
+'(' Punctuation
+'String' Name
+' ' Text.Whitespace
+'var2' Name
+':' Punctuation
+' ' Text.Whitespace
+'var1' Name
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_multiline_string.txt b/tests/snippets/java/test_multiline_string.txt
new file mode 100644
index 0000000..c7325b5
--- /dev/null
+++ b/tests/snippets/java/test_multiline_string.txt
@@ -0,0 +1,185 @@
+---input---
+public class Quine {
+ public static void main(String[] args) {
+ String textBlockQuotes = new String(new char[]{'"', '"', '"'});
+ char newLine = 10;
+ String teststring = "test123\n";
+ String source = """
+public class Quine {
+ public static void main(String[] args) {
+ String textBlockQuotes = new String(new char[]{'"', '"', '"'});
+ char newLine = 10;
+ String teststringinside = "hello my name is...\n\r";
+ String source = %s;
+ System.out.println(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes));
+ }
+}
+""";
+ System.out.println(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes));
+ String teststring2 = "Hello\n";
+ }
+}
+
+---tokens---
+'public' Keyword.Declaration
+' ' Text.Whitespace
+'class' Keyword.Declaration
+' ' Text
+'Quine' Name.Class
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'public' Keyword.Declaration
+' ' Text.Whitespace
+'static' Keyword.Declaration
+' ' Text.Whitespace
+'void' Keyword.Type
+' ' Text.Whitespace
+'main' Name.Function
+'(' Punctuation
+'String' Name
+'[' Operator
+']' Operator
+' ' Text.Whitespace
+'args' Name
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'\t\t' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'textBlockQuotes' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'new' Keyword
+' ' Text.Whitespace
+'String' Name
+'(' Punctuation
+'new' Keyword
+' ' Text.Whitespace
+'char' Keyword.Type
+'[' Operator
+']' Operator
+'{' Punctuation
+'\'"\'' Literal.String.Char
+',' Punctuation
+' ' Text.Whitespace
+'\'"\'' Literal.String.Char
+',' Punctuation
+' ' Text.Whitespace
+'\'"\'' Literal.String.Char
+'}' Punctuation
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\t\t' Text.Whitespace
+'char' Keyword.Type
+' ' Text.Whitespace
+'newLine' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'10' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
+
+'\t\t' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'teststring' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"' Literal.String
+'test123' Literal.String
+'\\' Literal.String
+'n' Literal.String
+'"' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+'\t ' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'source' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"""\n' Literal.String
+
+"public class Quine {\n\tpublic static void main(String[] args) {\n\t\tString textBlockQuotes = new String(new char[]{'" Literal.String
+'"' Literal.String
+"', '" Literal.String
+'"' Literal.String
+"', '" Literal.String
+'"' Literal.String
+"'});\n\t\tchar newLine = 10;\n\t\tString teststringinside = " Literal.String
+'"' Literal.String
+'hello my name is...' Literal.String
+'\\' Literal.String
+'n' Literal.String
+'\\' Literal.String
+'r' Literal.String
+'"' Literal.String
+';\n\t\tString source = %s;\n\t\tSystem.out.println(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes));\n\t}\n}\n' Literal.String
+
+'"""' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'System' Name
+'.' Punctuation
+'out' Name.Attribute
+'.' Punctuation
+'println' Name.Attribute
+'(' Punctuation
+'source' Name
+'.' Punctuation
+'formatted' Name.Attribute
+'(' Punctuation
+'textBlockQuotes' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'newLine' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'source' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'textBlockQuotes' Name
+')' Punctuation
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'teststring2' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"' Literal.String
+'Hello' Literal.String
+'\\' Literal.String
+'n' Literal.String
+'"' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+'\t' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_multiline_string_only.txt b/tests/snippets/java/test_multiline_string_only.txt
new file mode 100644
index 0000000..09d54ad
--- /dev/null
+++ b/tests/snippets/java/test_multiline_string_only.txt
@@ -0,0 +1,46 @@
+---input---
+ String source = """
+public class Quine {
+ public static void main(String[] args) {
+ String textBlockQuotes = new String(new char[]{'"', '"', '"'});
+ char newLine = 10;
+ String teststringinside = "hello my name is...\n\r";
+ String source = %s;
+ System.out.println(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes));
+ }
+}
+""";
+
+
+---tokens---
+'\t ' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'source' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"""\n' Literal.String
+
+"public class Quine {\n\tpublic static void main(String[] args) {\n\t\tString textBlockQuotes = new String(new char[]{'" Literal.String
+'"' Literal.String
+"', '" Literal.String
+'"' Literal.String
+"', '" Literal.String
+'"' Literal.String
+"'});\n\t\tchar newLine = 10;\n\t\tString teststringinside = " Literal.String
+'"' Literal.String
+'hello my name is...' Literal.String
+'\\' Literal.String
+'n' Literal.String
+'\\' Literal.String
+'r' Literal.String
+'"' Literal.String
+';\n\t\tString source = %s;\n\t\tSystem.out.println(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes));\n\t}\n}\n' Literal.String
+
+'"""' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_numeric_literals.txt b/tests/snippets/java/test_numeric_literals.txt
new file mode 100644
index 0000000..1dc933d
--- /dev/null
+++ b/tests/snippets/java/test_numeric_literals.txt
@@ -0,0 +1,34 @@
+---input---
+0 5L 9__542_72l 0xbEEf 0X9_A 0_35 01 0b0___101_0 0. .7_17F 3e-1_3d 1f 6_01.9e+3 0x.1Fp3 0XEP8D
+
+---tokens---
+'0' Literal.Number.Integer
+' ' Text.Whitespace
+'5L' Literal.Number.Integer
+' ' Text.Whitespace
+'9__542_72l' Literal.Number.Integer
+' ' Text.Whitespace
+'0xbEEf' Literal.Number.Hex
+' ' Text.Whitespace
+'0X9_A' Literal.Number.Hex
+' ' Text.Whitespace
+'0_35' Literal.Number.Oct
+' ' Text.Whitespace
+'01' Literal.Number.Oct
+' ' Text.Whitespace
+'0b0___101_0' Literal.Number.Bin
+' ' Text.Whitespace
+'0.' Literal.Number.Float
+' ' Text.Whitespace
+'.7_17F' Literal.Number.Float
+' ' Text.Whitespace
+'3e-1_3d' Literal.Number.Float
+' ' Text.Whitespace
+'1f' Literal.Number.Float
+' ' Text.Whitespace
+'6_01.9e+3' Literal.Number.Float
+' ' Text.Whitespace
+'0x.1Fp3' Literal.Number.Float
+' ' Text.Whitespace
+'0XEP8D' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/java/test_record.txt b/tests/snippets/java/test_record.txt
new file mode 100644
index 0000000..f4ca08b
--- /dev/null
+++ b/tests/snippets/java/test_record.txt
@@ -0,0 +1,67 @@
+---input---
+public record RecordTest() {}
+public static record RecordTest() {}
+record Person(String firstName, String lastName) {}
+String[] record = csvReader.getValues();
+
+
+---tokens---
+'public' Keyword.Declaration
+' ' Text.Whitespace
+'record' Keyword.Declaration
+' ' Text
+'RecordTest' Name.Class
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
+
+'public' Keyword.Declaration
+' ' Text.Whitespace
+'static' Keyword.Declaration
+' ' Text.Whitespace
+'record' Keyword.Declaration
+' ' Text
+'RecordTest' Name.Class
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
+
+'record' Keyword.Declaration
+' ' Text
+'Person' Name.Class
+'(' Punctuation
+'String' Name
+' ' Text.Whitespace
+'firstName' Name
+',' Punctuation
+' ' Text.Whitespace
+'String' Name
+' ' Text.Whitespace
+'lastName' Name
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
+
+'String' Name
+'[' Operator
+']' Operator
+' ' Text.Whitespace
+'record' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'csvReader' Name
+'.' Punctuation
+'getValues' Name.Attribute
+'(' Punctuation
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/js/super.txt b/tests/snippets/js/super.txt
new file mode 100644
index 0000000..1af2ad2
--- /dev/null
+++ b/tests/snippets/js/super.txt
@@ -0,0 +1,72 @@
+---input---
+super(member1, member2)
+
+super(member1,member2)
+
+super(member1,
+member2)
+
+super (member1, member2)
+
+super (member1,member2)
+
+super (member1,
+member2)
+
+---tokens---
+'super' Keyword
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+' ' Text.Whitespace
+'member2' Name.Other
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'super' Keyword
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+'member2' Name.Other
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'super' Keyword
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+'\n' Text.Whitespace
+
+'member2' Name.Other
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'super' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+' ' Text.Whitespace
+'member2' Name.Other
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'super' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+'member2' Name.Other
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'super' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'member1' Name.Other
+',' Punctuation
+'\n' Text.Whitespace
+
+'member2' Name.Other
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/jslt/test_sample.txt b/tests/snippets/jslt/test_sample.txt
new file mode 100644
index 0000000..73ad3fd
--- /dev/null
+++ b/tests/snippets/jslt/test_sample.txt
@@ -0,0 +1,83 @@
+---input---
+import "transforms/yellow.jslt" as yellow
+
+// Known valid types
+let valid-types = [ "SomeType" ]
+
+def foobar(arg) $arg.foobar
+
+{
+ "foobar": foobar(.),
+ "is-valid": contains(.type, $valid-types),
+ *: .
+}
+
+---tokens---
+'import' Keyword.Namespace
+' ' Text.Whitespace
+'"transforms/yellow.jslt"' Literal.String.Symbol
+' ' Text.Whitespace
+'as' Keyword.Namespace
+' ' Text.Whitespace
+'yellow' Name.Namespace
+'\n\n' Text.Whitespace
+
+'// Known valid types\n' Comment.Single
+
+'let' Keyword.Declaration
+' ' Text.Whitespace
+'valid-types' Name.Variable
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+' ' Text.Whitespace
+'"SomeType"' Literal.String.Double
+' ' Text.Whitespace
+']' Punctuation
+'\n\n' Text.Whitespace
+
+'def' Keyword.Declaration
+' ' Text.Whitespace
+'foobar' Name.Function
+'(' Punctuation
+'arg' Name.Variable
+')' Punctuation
+' ' Text.Whitespace
+'$arg' Name.Variable
+'.' Operator
+'foobar' Name
+'\n\n' Text.Whitespace
+
+'{' Punctuation
+'\n ' Text.Whitespace
+'"foobar"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'foobar' Name
+'(' Punctuation
+'.' Operator
+')' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'"is-valid"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'contains' Name.Builtin
+'(' Punctuation
+'.' Operator
+'type' Name
+',' Punctuation
+' ' Text.Whitespace
+'$valid-types' Name.Variable
+')' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'*' Operator
+':' Punctuation
+' ' Text.Whitespace
+'.' Operator
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/json/test_basic.txt b/tests/snippets/json/test_basic.txt
new file mode 100644
index 0000000..f93b91b
--- /dev/null
+++ b/tests/snippets/json/test_basic.txt
@@ -0,0 +1,30 @@
+---input---
+{"foo": "bar", "foo2": [1, 2, 3], "\u0123": "\u0123"}
+
+---tokens---
+'{' Punctuation
+'"foo"' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'"bar"' Literal.String.Double
+',' Punctuation
+' ' Text.Whitespace
+'"foo2"' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'3' Literal.Number.Integer
+'],' Punctuation
+' ' Text.Whitespace
+'"\\u0123"' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'"\\u0123"' Literal.String.Double
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/json/test_basic_bare.txt b/tests/snippets/json/test_basic_bare.txt
new file mode 100644
index 0000000..785d264
--- /dev/null
+++ b/tests/snippets/json/test_basic_bare.txt
@@ -0,0 +1,23 @@
+---input---
+"foo": "bar", "foo2": [1, 2, 3]
+
+---tokens---
+'"foo"' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'"bar"' Literal.String.Double
+',' Punctuation
+' ' Text.Whitespace
+'"foo2"' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'3' Literal.Number.Integer
+']' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia-repl/test_repl.txt b/tests/snippets/julia-repl/test_repl.txt
new file mode 100644
index 0000000..183de17
--- /dev/null
+++ b/tests/snippets/julia-repl/test_repl.txt
@@ -0,0 +1,51 @@
+Tests separating Julia's commands from output in REPL-like code blocks
+
+---input---
+julia> f(x) = sin(π/2x)
+f (generic function with 1 method)
+
+julia> @. f(1:2)
+2-element Vector{Float64}:
+ 1.0
+ 0.7071067811865475
+
+---tokens---
+'julia>' Generic.Prompt
+' ' Text.Whitespace
+'f' Name
+'(' Punctuation
+'x' Name
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'sin' Name
+'(' Punctuation
+'π' Name.Builtin
+'/' Operator
+'2' Literal.Number.Integer
+'x' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'f (generic function with 1 method)\n' Generic.Output
+
+'\n' Generic.Output
+
+'julia>' Generic.Prompt
+' ' Text.Whitespace
+'@.' Name.Decorator
+' ' Text.Whitespace
+'f' Name
+'(' Punctuation
+'1' Literal.Number.Integer
+':' Operator
+'2' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
+
+'2-element Vector{Float64}:\n' Generic.Output
+
+' 1.0\n' Generic.Output
+
+' 0.7071067811865475\n' Generic.Output
diff --git a/tests/snippets/julia/test_keywords.txt b/tests/snippets/julia/test_keywords.txt
new file mode 100644
index 0000000..10166d6
--- /dev/null
+++ b/tests/snippets/julia/test_keywords.txt
@@ -0,0 +1,101 @@
+# Test keywords are identified
+
+---input---
+mutable struct MutableType end
+struct ImmutableType end
+abstract type AbstractMyType end
+primitive type MyPrimitive 32 end
+(abstract, mutable, type) = true, π, missing
+
+abstract type AbstractMyType end
+primitive type MyPrimitive 32 end
+mutable struct MutableType end
+
+---tokens---
+'mutable' Keyword
+' ' Text.Whitespace
+'struct' Keyword
+' ' Text
+'MutableType' Keyword.Type
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'struct' Keyword
+' ' Text
+'ImmutableType' Keyword.Type
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'abstract' Keyword
+' ' Text.Whitespace
+'type' Keyword
+' ' Text
+'AbstractMyType' Keyword.Type
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'primitive' Keyword
+' ' Text.Whitespace
+'type' Keyword
+' ' Text
+'MyPrimitive' Keyword.Type
+' ' Text.Whitespace
+'32' Literal.Number.Integer
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'(' Punctuation
+'abstract' Name
+',' Punctuation
+' ' Text.Whitespace
+'mutable' Name
+',' Punctuation
+' ' Text.Whitespace
+'type' Name
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'true' Name.Builtin
+',' Punctuation
+' ' Text.Whitespace
+'π' Name.Builtin
+',' Punctuation
+' ' Text.Whitespace
+'missing' Name.Builtin
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'abstract' Keyword
+' ' Text.Whitespace
+'type' Keyword
+' ' Text
+'AbstractMyType' Keyword.Type
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'primitive' Keyword
+' \t' Text.Whitespace
+'type' Keyword
+' ' Text
+'MyPrimitive' Keyword.Type
+' ' Text.Whitespace
+'32' Literal.Number.Integer
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'mutable' Keyword
+' ' Text.Whitespace
+'struct' Keyword
+' ' Text
+'MutableType' Keyword.Type
+' ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_macros.txt b/tests/snippets/julia/test_macros.txt
new file mode 100644
index 0000000..43d67e0
--- /dev/null
+++ b/tests/snippets/julia/test_macros.txt
@@ -0,0 +1,56 @@
+# Test that macros are parsed, including ones which are defined as symbols
+
+---input---
+@generated function
+@. a + b
+@~ a + b
+@± a + b
+@mymacro(a, b)
+@+¹ᵀ a
+
+---tokens---
+'@generated' Name.Decorator
+' ' Text.Whitespace
+'function' Keyword
+'\n' Text.Whitespace
+
+'@.' Name.Decorator
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
+
+'@~' Name.Decorator
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
+
+'@±' Name.Decorator
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
+
+'@mymacro' Name.Decorator
+'(' Punctuation
+'a' Name
+',' Punctuation
+' ' Text.Whitespace
+'b' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'@+¹ᵀ' Name.Decorator
+' ' Text.Whitespace
+'a' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_names.txt b/tests/snippets/julia/test_names.txt
new file mode 100644
index 0000000..62c0b55
--- /dev/null
+++ b/tests/snippets/julia/test_names.txt
@@ -0,0 +1,148 @@
+# Test that the range of Julia variable names are correctly identified
+
+---input---
+a # single character variable
+a_simple_name
+_leading_underscore
+5implicit_mul
+6_more_mul
+nums1
+nums_2
+nameswith!
+multiple!!
+embedded!_inthemiddle
+embed!1
+prime_suffix′
+for_each # starts with keyword substring
+
+# variables with characters > \u00A1
+ð # category Ll
+Aʺ # category Lm -- \U02BA (MODIFIER LETTER DOUBLE PRIME), not \U2033 (DOUBLE PRIME)
+א # category Lo
+Ð # category Lu
+A̅ # category Mn -- \U0305 (COMBINING OVERLINE)
+ⅿ # category Nl -- \U217F (SMALL ROMAN NUMERAL ONE THOUSAND)
+A₁ # category No
+A² # category No
+€ # category Sc
+© # category So
+
+# number-like names
+𝟙 # category Nd
+𝟏 # category Nd
+
+---tokens---
+'a' Name
+' ' Text.Whitespace
+'# single character variable' Comment
+'\n' Text.Whitespace
+
+'a_simple_name' Name
+'\n' Text.Whitespace
+
+'_leading_underscore' Name
+'\n' Text.Whitespace
+
+'5' Literal.Number.Integer
+'implicit_mul' Name
+'\n' Text.Whitespace
+
+'6' Literal.Number.Integer
+'_more_mul' Name
+'\n' Text.Whitespace
+
+'nums1' Name
+'\n' Text.Whitespace
+
+'nums_2' Name
+'\n' Text.Whitespace
+
+'nameswith!' Name
+'\n' Text.Whitespace
+
+'multiple!!' Name
+'\n' Text.Whitespace
+
+'embedded!_inthemiddle' Name
+'\n' Text.Whitespace
+
+'embed!1' Name
+'\n' Text.Whitespace
+
+'prime_suffix′' Name
+'\n' Text.Whitespace
+
+'for_each' Name
+' ' Text.Whitespace
+'# starts with keyword substring' Comment
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# variables with characters > \\u00A1' Comment
+'\n' Text.Whitespace
+
+'ð' Name
+' ' Text.Whitespace
+'# category Ll' Comment
+'\n' Text.Whitespace
+
+'Aʺ' Name
+' ' Text.Whitespace
+'# category Lm -- \\U02BA (MODIFIER LETTER DOUBLE PRIME), not \\U2033 (DOUBLE PRIME)' Comment
+'\n' Text.Whitespace
+
+'א' Name
+' ' Text.Whitespace
+'# category Lo' Comment
+'\n' Text.Whitespace
+
+'Ð' Name
+' ' Text.Whitespace
+'# category Lu' Comment
+'\n' Text.Whitespace
+
+'A̅' Name
+' ' Text.Whitespace
+'# category Mn -- \\U0305 (COMBINING OVERLINE)' Comment
+'\n' Text.Whitespace
+
+'ⅿ' Name
+' ' Text.Whitespace
+'# category Nl -- \\U217F (SMALL ROMAN NUMERAL ONE THOUSAND)' Comment
+'\n' Text.Whitespace
+
+'A₁' Name
+' ' Text.Whitespace
+'# category No' Comment
+'\n' Text.Whitespace
+
+'A²' Name
+' ' Text.Whitespace
+'# category No' Comment
+'\n' Text.Whitespace
+
+'€' Name
+' ' Text.Whitespace
+'# category Sc' Comment
+'\n' Text.Whitespace
+
+'©' Name
+' ' Text.Whitespace
+'# category So' Comment
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# number-like names' Comment
+'\n' Text.Whitespace
+
+'𝟙' Name
+' ' Text.Whitespace
+'# category Nd' Comment
+'\n' Text.Whitespace
+
+'𝟏' Name
+' ' Text.Whitespace
+'# category Nd' Comment
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_numbers.txt b/tests/snippets/julia/test_numbers.txt
new file mode 100644
index 0000000..66f1fd0
--- /dev/null
+++ b/tests/snippets/julia/test_numbers.txt
@@ -0,0 +1,261 @@
+# Tests identification of number forms
+
+---input---
+# floats
+ 1e1 1e+1 1e-1
+1.1e1 1.1e+1 1.1e-1 .1e1 .1_1e1 1_1.1e1 1.1_1e1 1.1_11e1
+1.1E1 1.1E+1 1.1E-1 .1E1 .1_1E1 1_1.1E1 1.1_1E1 1.1_11E1
+1.1f1 1.1f+1 1.1f-1 .1f1 .1_1f1 1_1.1f1 1.1_1f1 1.1_11f1
+1E1 1E+1 1E-1
+1f1 1f+1 1f-1
+.1 1. 1.1 1.1_1 1.1_11 .1_1 .1_11 1_1.1_1
+# hex floats
+0x1p1 0xa_bp10 0x01_ap11 0x01_abp1
+0x1.1p1 0xA.Bp10 0x0.1_Ap9 0x0_1.Ap1 0x0_1.A_Bp9
+
+# integers
+1 01 10_1 10_11
+
+# non-decimal
+0xf 0xf_0 0xfff_000
+0o7 0o7_0 0o777_000
+0b1 0b1_0 0b111_000
+
+# invalid in Julia - out of range values
+0xg 0o8 0b2 0x1pA
+# invalid in Julia - no trailing underscores
+1_ 1.1_ 0xf_ 0o7_ 0b1_ 0xF_p1
+# parsed as juxtaposed numeral + variable in Julia (no underscores in exponents)
+1e1_1 1E1_1 1f1_1 0xfp1_1
+
+# not floats -- range-like expression parts
+1..1 ..1 1..
+
+---tokens---
+'# floats' Comment
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'1e1' Literal.Number.Float
+' ' Text.Whitespace
+'1e+1' Literal.Number.Float
+' ' Text.Whitespace
+'1e-1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.1e1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1e+1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1e-1' Literal.Number.Float
+' ' Text.Whitespace
+'.1e1' Literal.Number.Float
+' ' Text.Whitespace
+'.1_1e1' Literal.Number.Float
+' ' Text.Whitespace
+'1_1.1e1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_1e1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_11e1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.1E1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1E+1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1E-1' Literal.Number.Float
+' ' Text.Whitespace
+'.1E1' Literal.Number.Float
+' ' Text.Whitespace
+'.1_1E1' Literal.Number.Float
+' ' Text.Whitespace
+'1_1.1E1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_1E1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_11E1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.1f1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1f+1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1f-1' Literal.Number.Float
+' ' Text.Whitespace
+'.1f1' Literal.Number.Float
+' ' Text.Whitespace
+'.1_1f1' Literal.Number.Float
+' ' Text.Whitespace
+'1_1.1f1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_1f1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_11f1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1E1' Literal.Number.Float
+' ' Text.Whitespace
+'1E+1' Literal.Number.Float
+' ' Text.Whitespace
+'1E-1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1f1' Literal.Number.Float
+' ' Text.Whitespace
+'1f+1' Literal.Number.Float
+' ' Text.Whitespace
+'1f-1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'.1' Literal.Number.Float
+' ' Text.Whitespace
+'1.' Literal.Number.Float
+' ' Text.Whitespace
+'1.1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_1' Literal.Number.Float
+' ' Text.Whitespace
+'1.1_11' Literal.Number.Float
+' ' Text.Whitespace
+'.1_1' Literal.Number.Float
+' ' Text.Whitespace
+'.1_11' Literal.Number.Float
+' ' Text.Whitespace
+'1_1.1_1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'# hex floats' Comment
+'\n' Text.Whitespace
+
+'0x1p1' Literal.Number.Float
+' ' Text.Whitespace
+'0xa_bp10' Literal.Number.Float
+' ' Text.Whitespace
+'0x01_ap11' Literal.Number.Float
+' ' Text.Whitespace
+'0x01_abp1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'0x1.1p1' Literal.Number.Float
+' ' Text.Whitespace
+'0xA.Bp10' Literal.Number.Float
+' ' Text.Whitespace
+'0x0.1_Ap9' Literal.Number.Float
+' ' Text.Whitespace
+'0x0_1.Ap1' Literal.Number.Float
+' ' Text.Whitespace
+'0x0_1.A_Bp9' Literal.Number.Float
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# integers' Comment
+'\n' Text.Whitespace
+
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+'01' Literal.Number.Integer
+' ' Text.Whitespace
+'10_1' Literal.Number.Integer
+' ' Text.Whitespace
+'10_11' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# non-decimal' Comment
+'\n' Text.Whitespace
+
+'0xf' Literal.Number.Hex
+' ' Text.Whitespace
+'0xf_0' Literal.Number.Hex
+' ' Text.Whitespace
+'0xfff_000' Literal.Number.Hex
+'\n' Text.Whitespace
+
+'0o7' Literal.Number.Oct
+' ' Text.Whitespace
+'0o7_0' Literal.Number.Oct
+' ' Text.Whitespace
+'0o777_000' Literal.Number.Oct
+'\n' Text.Whitespace
+
+'0b1' Literal.Number.Bin
+' ' Text.Whitespace
+'0b1_0' Literal.Number.Bin
+' ' Text.Whitespace
+'0b111_000' Literal.Number.Bin
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# invalid in Julia - out of range values' Comment
+'\n' Text.Whitespace
+
+'0' Literal.Number.Integer
+'xg' Name
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+'o8' Name
+' ' Text.Whitespace
+'0' Literal.Number.Integer
+'b2' Name
+' ' Text.Whitespace
+'0x1' Literal.Number.Hex
+'pA' Name
+'\n' Text.Whitespace
+
+'# invalid in Julia - no trailing underscores' Comment
+'\n' Text.Whitespace
+
+'1' Literal.Number.Integer
+'_' Name
+' ' Text.Whitespace
+'1.1' Literal.Number.Float
+'_' Name
+' ' Text.Whitespace
+'0xf' Literal.Number.Hex
+'_' Name
+' ' Text.Whitespace
+'0o7' Literal.Number.Oct
+'_' Name
+' ' Text.Whitespace
+'0b1' Literal.Number.Bin
+'_' Name
+' ' Text.Whitespace
+'0xF' Literal.Number.Hex
+'_p1' Name
+'\n' Text.Whitespace
+
+'# parsed as juxtaposed numeral + variable in Julia (no underscores in exponents)' Comment
+'\n' Text.Whitespace
+
+'1e1' Literal.Number.Float
+'_1' Name
+' ' Text.Whitespace
+'1E1' Literal.Number.Float
+'_1' Name
+' ' Text.Whitespace
+'1f1' Literal.Number.Float
+'_1' Name
+' ' Text.Whitespace
+'0xfp1' Literal.Number.Float
+'_1' Name
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# not floats -- range-like expression parts' Comment
+'\n' Text.Whitespace
+
+'1' Literal.Number.Integer
+'..' Operator
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+'..' Operator
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'..' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_operators.txt b/tests/snippets/julia/test_operators.txt
new file mode 100644
index 0000000..c3c0f9f
--- /dev/null
+++ b/tests/snippets/julia/test_operators.txt
@@ -0,0 +1,172 @@
+# Test that operators --- dotted and unicode --- are identified correctly.
+
+---input---
+a += b.c
+a .÷= .~b.c
+a = !b ⋆ c!
+a = b ? c : d ⊕ e
+a = √(5)
+a -> (a...) .+ 1
+a \ b
+1..2
+a = a === b
+a <: T
+a >: T
+a::T
+[adjoint]'
+(identity)''
+adjoint'''
+transpose'ᵀ
+suffixed +¹ operator
+suffixed +¹²³ operator
+
+---tokens---
+'a' Name
+' ' Text.Whitespace
+'+=' Operator
+' ' Text.Whitespace
+'b' Name
+'.' Operator
+'c' Name
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'.÷=' Operator
+' ' Text.Whitespace
+'.~' Operator
+'b' Name
+'.' Operator
+'c' Name
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'!' Operator
+'b' Name
+' ' Text.Whitespace
+'⋆' Operator
+' ' Text.Whitespace
+'c!' Name
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'b' Name
+' ' Text.Whitespace
+'?' Operator
+' ' Text.Whitespace
+'c' Name
+' ' Text.Whitespace
+':' Operator
+' ' Text.Whitespace
+'d' Name
+' ' Text.Whitespace
+'⊕' Operator
+' ' Text.Whitespace
+'e' Name
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'√' Operator
+'(' Punctuation
+'5' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'->' Operator
+' ' Text.Whitespace
+'(' Punctuation
+'a' Name
+'...' Operator
+')' Punctuation
+' ' Text.Whitespace
+'.+' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'\\' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
+
+'1' Literal.Number.Integer
+'..' Operator
+'2' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'a' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'===' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
+
+'a' Keyword.Type
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'a' Keyword.Type
+' ' Text.Whitespace
+'>:' Operator
+' ' Text.Whitespace
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'a' Name
+'::' Operator
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'[' Punctuation
+'adjoint' Name
+']' Punctuation
+"'" Operator
+'\n' Text.Whitespace
+
+'(' Punctuation
+'identity' Name
+')' Punctuation
+"''" Operator
+'\n' Text.Whitespace
+
+'adjoint' Name
+"'''" Operator
+'\n' Text.Whitespace
+
+'transpose' Name
+"'ᵀ" Operator
+'\n' Text.Whitespace
+
+'suffixed' Name
+' ' Text.Whitespace
+'+¹' Operator
+' ' Text.Whitespace
+'operator' Name
+'\n' Text.Whitespace
+
+'suffixed' Name
+' ' Text.Whitespace
+'+¹²³' Operator
+' ' Text.Whitespace
+'operator' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_strings.txt b/tests/snippets/julia/test_strings.txt
new file mode 100644
index 0000000..d5f91bf
--- /dev/null
+++ b/tests/snippets/julia/test_strings.txt
@@ -0,0 +1,225 @@
+# Tests string forms
+
+---input---
+"global function"
+"An $interpolated variable"
+"An $(a + 1) expression"
+"""a"""
+"""
+global function
+de e f
+"inner string"
+"""
+raw"\\ a \" $interp $(1 + 1) \""
+raw"""
+"inner string"
+$interp
+$(1 + 1)
+"""
+# commented "string"
+
+@sprintf "%0.2f" var
+v"1.0"
+var"#nonstandard#"
+
+r"^[abs]+$"m
+arbi"trary"suff
+arbi"trary"1234
+
+`global function`
+`abc \` \$ $interpolated`
+`abc $(a + 1)`
+```a```
+```
+global function
+"thing" ` \$
+`now` $(now())
+```
+# commented `command`
+
+arbi`trary`suff
+arbi`trary`1234
+
+---tokens---
+'"' Literal.String
+'global function' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'An ' Literal.String
+'$interpolated' Literal.String.Interpol
+' variable' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'An ' Literal.String
+'$' Literal.String.Interpol
+'(' Punctuation
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+')' Punctuation
+' expression' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'"""' Literal.String
+'a' Literal.String
+'"""' Literal.String
+'\n' Text.Whitespace
+
+'"""' Literal.String
+'\nglobal function\nde e f\n' Literal.String
+
+'"' Literal.String
+'inner string' Literal.String
+'"' Literal.String
+'\n' Literal.String
+
+'"""' Literal.String
+'\n' Text.Whitespace
+
+'raw' Literal.String.Affix
+'"' Literal.String
+'\\\\ a ' Literal.String
+'\\"' Literal.String.Escape
+' $interp $(1 + 1) ' Literal.String
+'\\"' Literal.String.Escape
+'"' Literal.String
+'\n' Text.Whitespace
+
+'raw' Literal.String.Affix
+'"""' Literal.String
+'\n"inner string"\n$interp\n$(1 + 1)\n' Literal.String
+
+'"""' Literal.String
+'\n' Text.Whitespace
+
+'# commented "string"' Comment
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'@sprintf' Name.Decorator
+' ' Text.Whitespace
+'"' Literal.String
+'%0.2f' Literal.String.Interpol
+'"' Literal.String
+' ' Text.Whitespace
+'var' Name
+'\n' Text.Whitespace
+
+'v' Literal.String.Affix
+'"' Literal.String
+'1.0' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'var' Literal.String.Affix
+'"' Literal.String
+'#nonstandard#' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'r' Literal.String.Affix
+'"' Literal.String.Regex
+'^[abs]+$' Literal.String.Regex
+'"' Literal.String.Regex
+'m' Literal.String.Affix
+'\n' Text.Whitespace
+
+'arbi' Literal.String.Affix
+'"' Literal.String
+'trary' Literal.String
+'"' Literal.String
+'suff' Literal.String.Affix
+'\n' Text.Whitespace
+
+'arbi' Literal.String.Affix
+'"' Literal.String
+'trary' Literal.String
+'"' Literal.String
+'1234' Literal.String.Affix
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'`' Literal.String.Backtick
+'global function' Literal.String.Backtick
+'`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'`' Literal.String.Backtick
+'abc ' Literal.String.Backtick
+'\\`' Literal.String.Escape
+' ' Literal.String.Backtick
+'\\$' Literal.String.Escape
+' ' Literal.String.Backtick
+'$interpolated' Literal.String.Interpol
+'`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'`' Literal.String.Backtick
+'abc ' Literal.String.Backtick
+'$' Literal.String.Interpol
+'(' Punctuation
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+')' Punctuation
+'`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'```' Literal.String.Backtick
+'a' Literal.String.Backtick
+'```' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'```' Literal.String.Backtick
+'\nglobal function\n"thing" ' Literal.String.Backtick
+'`' Literal.String.Backtick
+' ' Literal.String.Backtick
+'\\$' Literal.String.Escape
+'\n' Literal.String.Backtick
+
+'`' Literal.String.Backtick
+'now' Literal.String.Backtick
+'`' Literal.String.Backtick
+' ' Literal.String.Backtick
+'$' Literal.String.Interpol
+'(' Punctuation
+'now' Name
+'(' Punctuation
+')' Punctuation
+')' Punctuation
+'\n' Literal.String.Backtick
+
+'```' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'# commented `command`' Comment
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'arbi' Literal.String.Affix
+'`' Literal.String.Backtick
+'trary' Literal.String.Backtick
+'`' Literal.String.Backtick
+'suff' Literal.String.Affix
+'\n' Text.Whitespace
+
+'arbi' Literal.String.Affix
+'`' Literal.String.Backtick
+'trary' Literal.String.Backtick
+'`' Literal.String.Backtick
+'1234' Literal.String.Affix
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_symbols.txt b/tests/snippets/julia/test_symbols.txt
new file mode 100644
index 0000000..37b4f83
--- /dev/null
+++ b/tests/snippets/julia/test_symbols.txt
@@ -0,0 +1,78 @@
+# Tests that symbols are parsed as special literals
+
+---input---
+:abc_123
+:abc_def
+:α
+Val{:mysymbol}
+
+# non-symbols
+a:b
+1:b
+1.:b
+a::T
+a<:T
+a>:T
+UInt(1):UInt(2)
+
+---tokens---
+':abc_123' Literal.String.Symbol
+'\n' Text.Whitespace
+
+':abc_def' Literal.String.Symbol
+'\n' Text.Whitespace
+
+':α' Literal.String.Symbol
+'\n' Text.Whitespace
+
+'Val' Keyword.Type
+'{' Punctuation
+':mysymbol' Literal.String.Symbol
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'# non-symbols' Comment
+'\n' Text.Whitespace
+
+'a' Name
+':' Operator
+'b' Name
+'\n' Text.Whitespace
+
+'1' Literal.Number.Integer
+':' Operator
+'b' Name
+'\n' Text.Whitespace
+
+'1.' Literal.Number.Float
+':' Operator
+'b' Name
+'\n' Text.Whitespace
+
+'a' Name
+'::' Operator
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'a' Keyword.Type
+'<:' Operator
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'a' Keyword.Type
+'>:' Operator
+'T' Keyword.Type
+'\n' Text.Whitespace
+
+'UInt' Keyword.Type
+'(' Punctuation
+'1' Literal.Number.Integer
+')' Punctuation
+':' Operator
+'UInt' Keyword.Type
+'(' Punctuation
+'2' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_types.txt b/tests/snippets/julia/test_types.txt
new file mode 100644
index 0000000..16267fd
--- /dev/null
+++ b/tests/snippets/julia/test_types.txt
@@ -0,0 +1,196 @@
+# Tests identifying names which must be types from context
+
+---input---
+Union{}
+MyType{Nothing, Any}
+f(::Union{T,S}) where S where T = 1
+f(::T) where {T} = 1
+f(::Type{<:T}) = 1
+f(::AT) where AT <: AbstractArray{MyType,1} = 1
+f(::Val{:named}) = 1
+f(::typeof(sin)) = 1
+MyInt <: Integer
+Number >: MyInt
+AT{T,1} <: B
+B>:AT{T,1}
+A <: f(B)
+g(C) <: T
+
+---tokens---
+'Union' Keyword.Type
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
+
+'MyType' Keyword.Type
+'{' Punctuation
+'Nothing' Keyword.Type
+',' Punctuation
+' ' Text.Whitespace
+'Any' Keyword.Type
+'}' Punctuation
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'Union' Keyword.Type
+'{' Punctuation
+'T' Keyword.Type
+',' Punctuation
+'S' Keyword.Type
+'}' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'where' Keyword
+' ' Text.Whitespace
+'S' Keyword.Type
+' ' Text.Whitespace
+'where' Keyword
+' ' Text.Whitespace
+'T' Keyword.Type
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'T' Keyword.Type
+')' Punctuation
+' ' Text.Whitespace
+'where' Keyword
+' ' Text.Whitespace
+'{' Punctuation
+'T' Keyword.Type
+'}' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'Type' Keyword.Type
+'{' Punctuation
+'<:' Operator
+'T' Keyword.Type
+'}' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'AT' Keyword.Type
+')' Punctuation
+' ' Text.Whitespace
+'where' Keyword
+' ' Text.Whitespace
+'AT' Keyword.Type
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'AbstractArray' Keyword.Type
+'{' Punctuation
+'MyType' Keyword.Type
+',' Punctuation
+'1' Literal.Number.Integer
+'}' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'Val' Keyword.Type
+'{' Punctuation
+':named' Literal.String.Symbol
+'}' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'f' Name
+'(' Punctuation
+'::' Operator
+'typeof' Name
+'(' Punctuation
+'sin' Name
+')' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'MyInt' Keyword.Type
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'Integer' Keyword.Type
+'\n' Text.Whitespace
+
+'Number' Keyword.Type
+' ' Text.Whitespace
+'>:' Operator
+' ' Text.Whitespace
+'MyInt' Keyword.Type
+'\n' Text.Whitespace
+
+'AT' Keyword.Type
+'{' Punctuation
+'T' Keyword.Type
+',' Punctuation
+'1' Literal.Number.Integer
+'}' Punctuation
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'B' Keyword.Type
+'\n' Text.Whitespace
+
+'B' Keyword.Type
+'>:' Operator
+'AT' Keyword.Type
+'{' Punctuation
+'T' Keyword.Type
+',' Punctuation
+'1' Literal.Number.Integer
+'}' Punctuation
+'\n' Text.Whitespace
+
+'A' Keyword.Type
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'f' Name
+'(' Punctuation
+'B' Name
+')' Punctuation
+'\n' Text.Whitespace
+
+'g' Name
+'(' Punctuation
+'C' Name
+')' Punctuation
+' ' Text.Whitespace
+'<:' Operator
+' ' Text.Whitespace
+'T' Keyword.Type
+'\n' Text.Whitespace
diff --git a/tests/snippets/julia/test_unicode.txt b/tests/snippets/julia/test_unicode.txt
new file mode 100644
index 0000000..6b2508a
--- /dev/null
+++ b/tests/snippets/julia/test_unicode.txt
@@ -0,0 +1,37 @@
+# Test that unicode character, √, in an expression is recognized
+
+---input---
+s = √((1/n) * sum(count .^ 2) - mu .^2)
+
+---tokens---
+'s' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'√' Operator
+'(' Punctuation
+'(' Punctuation
+'1' Literal.Number.Integer
+'/' Operator
+'n' Name
+')' Punctuation
+' ' Text.Whitespace
+'*' Operator
+' ' Text.Whitespace
+'sum' Name
+'(' Punctuation
+'count' Name
+' ' Text.Whitespace
+'.^' Operator
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+')' Punctuation
+' ' Text.Whitespace
+'-' Operator
+' ' Text.Whitespace
+'mu' Name
+' ' Text.Whitespace
+'.^' Operator
+'2' Literal.Number.Integer
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_can_cope_generics_in_destructuring.txt b/tests/snippets/kotlin/test_can_cope_generics_in_destructuring.txt
new file mode 100644
index 0000000..8fc7f3d
--- /dev/null
+++ b/tests/snippets/kotlin/test_can_cope_generics_in_destructuring.txt
@@ -0,0 +1,27 @@
+---input---
+val (a: List<Something>, b: Set<Wobble>) =
+
+---tokens---
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'(' Punctuation
+'a' Name.Variable
+':' Punctuation
+' ' Text.Whitespace
+'List' Name
+'<' Operator
+'Something' Name
+'>' Operator
+',' Punctuation
+' ' Text.Whitespace
+'b' Name.Variable
+':' Punctuation
+' ' Text.Whitespace
+'Set' Name
+'<' Operator
+'Wobble' Name
+'>' Operator
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_can_cope_with_backtick_names_in_functions.txt b/tests/snippets/kotlin/test_can_cope_with_backtick_names_in_functions.txt
new file mode 100644
index 0000000..c89c0ef
--- /dev/null
+++ b/tests/snippets/kotlin/test_can_cope_with_backtick_names_in_functions.txt
@@ -0,0 +1,8 @@
+---input---
+fun `wo bble`
+
+---tokens---
+'fun' Keyword.Declaration
+' ' Text.Whitespace
+'`wo bble`' Name.Function
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_can_cope_with_commas_and_dashes_in_backtick_Names.txt b/tests/snippets/kotlin/test_can_cope_with_commas_and_dashes_in_backtick_Names.txt
new file mode 100644
index 0000000..7b2aed7
--- /dev/null
+++ b/tests/snippets/kotlin/test_can_cope_with_commas_and_dashes_in_backtick_Names.txt
@@ -0,0 +1,8 @@
+---input---
+fun `wo,-bble`
+
+---tokens---
+'fun' Keyword.Declaration
+' ' Text.Whitespace
+'`wo,-bble`' Name.Function
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_can_cope_with_destructuring.txt b/tests/snippets/kotlin/test_can_cope_with_destructuring.txt
new file mode 100644
index 0000000..3db6ed9
--- /dev/null
+++ b/tests/snippets/kotlin/test_can_cope_with_destructuring.txt
@@ -0,0 +1,16 @@
+---input---
+val (a, b) =
+
+---tokens---
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'(' Punctuation
+'a' Name.Variable
+',' Punctuation
+' ' Text.Whitespace
+'b' Name.Variable
+')' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_can_cope_with_generics.txt b/tests/snippets/kotlin/test_can_cope_with_generics.txt
new file mode 100644
index 0000000..09eb61f
--- /dev/null
+++ b/tests/snippets/kotlin/test_can_cope_with_generics.txt
@@ -0,0 +1,34 @@
+---input---
+inline fun <reified T : ContractState> VaultService.queryBy(): Vault.Page<T> {
+
+---tokens---
+'inline' Keyword.Declaration
+' ' Text.Whitespace
+'fun' Keyword.Declaration
+' ' Text.Whitespace
+'<' Operator
+'reified' Keyword
+' ' Text.Whitespace
+'T' Name
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'ContractState' Name
+'>' Operator
+' ' Text.Whitespace
+'VaultService' Name
+'.' Punctuation
+'queryBy' Name.Function
+'(' Punctuation
+')' Punctuation
+':' Punctuation
+' ' Text.Whitespace
+'Vault' Name
+'.' Punctuation
+'Page' Name.Attribute
+'<' Operator
+'T' Name
+'>' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_modifier_keyword.txt b/tests/snippets/kotlin/test_modifier_keyword.txt
new file mode 100644
index 0000000..730c0a5
--- /dev/null
+++ b/tests/snippets/kotlin/test_modifier_keyword.txt
@@ -0,0 +1,18 @@
+---input---
+data class A(val data: String)
+
+---tokens---
+'data' Keyword.Declaration
+' ' Text.Whitespace
+'class' Keyword.Declaration
+' ' Text.Whitespace
+'A' Name.Class
+'(' Punctuation
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'data' Name.Variable
+':' Punctuation
+' ' Text.Whitespace
+'String' Keyword.Type
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_should_cope_with_multiline_comments.txt b/tests/snippets/kotlin/test_should_cope_with_multiline_comments.txt
new file mode 100644
index 0000000..33ec204
--- /dev/null
+++ b/tests/snippets/kotlin/test_should_cope_with_multiline_comments.txt
@@ -0,0 +1,12 @@
+---input---
+"""
+this
+is
+a
+comment"""
+
+---tokens---
+'"""' Literal.String
+'\nthis\nis\na\ncomment' Literal.String
+'"""' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/kotlin/test_string_interpolation.txt b/tests/snippets/kotlin/test_string_interpolation.txt
new file mode 100644
index 0000000..04ea254
--- /dev/null
+++ b/tests/snippets/kotlin/test_string_interpolation.txt
@@ -0,0 +1,35 @@
+---input---
+val something = "something"
+"Here is $something"
+"Here is ${something.toUpperList()}"
+
+---tokens---
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'something' Name.Variable
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"' Literal.String
+'something' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'Here is ' Literal.String
+'$' Literal.String.Interpol
+'something' Name
+'"' Literal.String
+'\n' Text.Whitespace
+
+'"' Literal.String
+'Here is ' Literal.String
+'${' Literal.String.Interpol
+'something' Name
+'.' Punctuation
+'toUpperList' Name.Attribute
+'(' Punctuation
+')' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/less/test_single_line_comments.txt b/tests/snippets/less/test_single_line_comments.txt
new file mode 100644
index 0000000..a900644
--- /dev/null
+++ b/tests/snippets/less/test_single_line_comments.txt
@@ -0,0 +1,21 @@
+---input---
+.font-light {
+ font-weight: 100; // Comment
+}
+
+---tokens---
+'.' Punctuation
+'font-light' Name.Class
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'font-weight' Keyword
+':' Punctuation
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+';' Punctuation
+' ' Text.Whitespace
+'// Comment\n' Comment.Single
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/mason/test_handles_tags_correctly.txt b/tests/snippets/mason/test_handles_tags_correctly.txt
new file mode 100644
index 0000000..0eb215d
--- /dev/null
+++ b/tests/snippets/mason/test_handles_tags_correctly.txt
@@ -0,0 +1,69 @@
+---input---
+<%class>
+has 'foo';
+has 'bar' => (required => 1);
+has 'baz' => (isa => 'Int', default => 17);
+</%class>
+
+---tokens---
+'<%class>' Name.Tag
+'\n' Text.Whitespace
+
+'' Name
+'has' Name
+' ' Text.Whitespace
+"'foo'" Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+'' Name
+'has' Name
+' ' Text.Whitespace
+"'bar'" Literal.String
+' ' Text.Whitespace
+'=' Operator
+'>' Operator
+' ' Text.Whitespace
+'(' Punctuation
+'' Name
+'required' Name
+' ' Text.Whitespace
+'=' Operator
+'>' Operator
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'' Name
+'has' Name
+' ' Text.Whitespace
+"'baz'" Literal.String
+' ' Text.Whitespace
+'=' Operator
+'>' Operator
+' ' Text.Whitespace
+'(' Punctuation
+'' Name
+'isa' Name
+' ' Text.Whitespace
+'=' Operator
+'>' Operator
+' ' Text.Whitespace
+"'Int'" Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'' Name
+'default' Name
+' ' Text.Whitespace
+'=' Operator
+'>' Operator
+' ' Text.Whitespace
+'17' Literal.Number.Integer
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'</%class>' Name.Tag
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_classes_with_properties.txt b/tests/snippets/matlab/test_classes_with_properties.txt
new file mode 100644
index 0000000..7de838eb
--- /dev/null
+++ b/tests/snippets/matlab/test_classes_with_properties.txt
@@ -0,0 +1,105 @@
+---input---
+classdef Name < dynamicprops
+ properties
+ % i am a comment
+ name1
+ name2
+ end
+ properties (Constant = true, SetAccess = protected)
+ % i too am a comment
+ matrix = [0, 1, 2];
+ string = 'i am a string'
+ end
+ methods
+ % i am also a comment
+ function self = Name()
+ % i am a comment inside a constructor
+ end
+ end
+end
+
+---tokens---
+'classdef' Keyword
+' ' Text.Whitespace
+'Name' Name
+' ' Text.Whitespace
+'<' Operator
+' ' Text.Whitespace
+'dynamicprops' Keyword
+'\n ' Text.Whitespace
+'properties' Keyword
+'\n ' Text.Whitespace
+'% i am a comment' Comment
+'\n ' Text.Whitespace
+'name1' Name
+'\n ' Text.Whitespace
+'name2' Name
+'\n ' Text.Whitespace
+'end' Keyword
+'\n ' Text.Whitespace
+'properties' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'Constant' Name.Builtin
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'true' Keyword
+',' Punctuation
+' ' Text.Whitespace
+'SetAccess' Name.Builtin
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'protected' Keyword
+')' Punctuation
+'\n ' Text.Whitespace
+'% i too am a comment' Comment
+'\n ' Text.Whitespace
+'matrix' Name
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'0' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+']' Punctuation
+';' Punctuation
+'\n ' Text.Whitespace
+'string' Name
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+"'" Literal.String
+"i am a string'" Literal.String
+'\n ' Text.Whitespace
+'end' Keyword
+'\n ' Text.Whitespace
+'methods' Keyword
+'\n ' Text.Whitespace
+'% i am also a comment' Comment
+'\n ' Text.Whitespace
+'function' Keyword
+' ' Text.Whitespace
+'self' Text
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'Name' Name.Function
+'(' Punctuation
+')' Punctuation
+'\n ' Text.Whitespace
+'% i am a comment inside a constructor' Comment
+'\n ' Text.Whitespace
+'end' Keyword
+'\n ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_command_mode.txt b/tests/snippets/matlab/test_command_mode.txt
new file mode 100644
index 0000000..e9a8c11
--- /dev/null
+++ b/tests/snippets/matlab/test_command_mode.txt
@@ -0,0 +1,12 @@
+# MATLAB allows char function arguments to not be enclosed by parentheses
+# or contain quote characters, as long as they are space separated. Test
+# that one common such function is formatted appropriately.
+
+---input---
+help sin
+
+---tokens---
+'help' Name.Builtin
+' ' Text.Whitespace
+'sin' Name.Builtin
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_comment_after_continuation.txt b/tests/snippets/matlab/test_comment_after_continuation.txt
new file mode 100644
index 0000000..baf88e3
--- /dev/null
+++ b/tests/snippets/matlab/test_comment_after_continuation.txt
@@ -0,0 +1,25 @@
+# Test that text after the line continuation ellipses is marked as a comment.
+
+---input---
+set('T',300,... a comment
+'P',101325);
+
+---tokens---
+'set' Name.Builtin
+'(' Punctuation
+"'" Literal.String
+"T'" Literal.String
+',' Punctuation
+'300' Literal.Number.Integer
+',' Punctuation
+'...' Keyword
+' a comment' Comment
+'\n' Text.Whitespace
+
+"'" Literal.String
+"P'" Literal.String
+',' Punctuation
+'101325' Literal.Number.Integer
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_dot_operator.txt b/tests/snippets/matlab/test_dot_operator.txt
new file mode 100644
index 0000000..b858cd3
--- /dev/null
+++ b/tests/snippets/matlab/test_dot_operator.txt
@@ -0,0 +1,10 @@
+# 1./x is (1)(./)(x), not (1.)(/)(x)
+
+---input---
+1./x
+
+---tokens---
+'1' Literal.Number.Integer
+'./' Operator
+'x' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_keywords_ended_by_newline.txt b/tests/snippets/matlab/test_keywords_ended_by_newline.txt
new file mode 100644
index 0000000..59dca03
--- /dev/null
+++ b/tests/snippets/matlab/test_keywords_ended_by_newline.txt
@@ -0,0 +1,36 @@
+# Test that keywords on their own line are marked as keywords.
+
+---input---
+if x > 100
+ disp('x > 100')
+else
+ disp('x < 100')
+end
+
+---tokens---
+'if' Keyword
+' ' Text.Whitespace
+'x' Name
+' ' Text.Whitespace
+'>' Operator
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+'\n ' Text.Whitespace
+'disp' Name.Builtin
+'(' Punctuation
+"'" Literal.String
+"x > 100'" Literal.String
+')' Punctuation
+'\n' Text.Whitespace
+
+'else' Keyword
+'\n ' Text.Whitespace
+'disp' Name.Builtin
+'(' Punctuation
+"'" Literal.String
+"x < 100'" Literal.String
+')' Punctuation
+'\n' Text.Whitespace
+
+'end' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_line_continuation.txt b/tests/snippets/matlab/test_line_continuation.txt
new file mode 100644
index 0000000..1e47368
--- /dev/null
+++ b/tests/snippets/matlab/test_line_continuation.txt
@@ -0,0 +1,25 @@
+# Test that line continuation by ellipses does not produce generic
+# output on the second line.
+
+---input---
+set('T',300,...
+'P',101325);
+
+---tokens---
+'set' Name.Builtin
+'(' Punctuation
+"'" Literal.String
+"T'" Literal.String
+',' Punctuation
+'300' Literal.Number.Integer
+',' Punctuation
+'...' Keyword
+'\n' Text.Whitespace
+
+"'" Literal.String
+"P'" Literal.String
+',' Punctuation
+'101325' Literal.Number.Integer
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_multiple_spaces_variable_assignment.txt b/tests/snippets/matlab/test_multiple_spaces_variable_assignment.txt
new file mode 100644
index 0000000..ec5ac24
--- /dev/null
+++ b/tests/snippets/matlab/test_multiple_spaces_variable_assignment.txt
@@ -0,0 +1,13 @@
+# Test that multiple spaces with an equal sign doesn't get formatted to a string.
+
+---input---
+x = 100;
+
+---tokens---
+'x' Name
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_one_space_assignment.txt b/tests/snippets/matlab/test_one_space_assignment.txt
new file mode 100644
index 0000000..ceafb6e
--- /dev/null
+++ b/tests/snippets/matlab/test_one_space_assignment.txt
@@ -0,0 +1,13 @@
+# Test that one space before an equal sign is formatted correctly.
+
+---input---
+x = 100;
+
+---tokens---
+'x' Name
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_operator_multiple_space.txt b/tests/snippets/matlab/test_operator_multiple_space.txt
new file mode 100644
index 0000000..e13d3a3
--- /dev/null
+++ b/tests/snippets/matlab/test_operator_multiple_space.txt
@@ -0,0 +1,13 @@
+# Test that multiple spaces with an operator doesn't get formatted to a string.
+
+---input---
+x > 100;
+
+---tokens---
+'x' Name
+' ' Text.Whitespace
+'>' Operator
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlab/test_single_line.txt b/tests/snippets/matlab/test_single_line.txt
new file mode 100644
index 0000000..72a48f8
--- /dev/null
+++ b/tests/snippets/matlab/test_single_line.txt
@@ -0,0 +1,18 @@
+---input---
+set('T',300,'P',101325);
+
+---tokens---
+'set' Name.Builtin
+'(' Punctuation
+"'" Literal.String
+"T'" Literal.String
+',' Punctuation
+'300' Literal.Number.Integer
+',' Punctuation
+"'" Literal.String
+"P'" Literal.String
+',' Punctuation
+'101325' Literal.Number.Integer
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/matlabsession/test_wrong_continuation.txt b/tests/snippets/matlabsession/test_wrong_continuation.txt
new file mode 100644
index 0000000..6ea3e31
--- /dev/null
+++ b/tests/snippets/matlabsession/test_wrong_continuation.txt
@@ -0,0 +1,18 @@
+---input---
+>> foo()
+bar
+...
+baz
+
+---tokens---
+'>> ' Generic.Prompt
+'foo' Name
+'(' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'bar\n' Generic.Output
+
+'...\n' Generic.Output
+
+'baz\n' Generic.Output
diff --git a/tests/snippets/mcfunction/commenting.txt b/tests/snippets/mcfunction/commenting.txt
new file mode 100644
index 0000000..7a5a37d
--- /dev/null
+++ b/tests/snippets/mcfunction/commenting.txt
@@ -0,0 +1,173 @@
+---input---
+#> Get: #rx.playerdb:api/v2/get
+#>
+#> @input
+#> $in.uid rx.io
+#>
+#> @output
+#> rx.playerdb:io player
+#
+# Selects data inside the database and copies to rx.playerdb:io player
+# See #api/v2/select for more info..
+#
+#* Note: something, something, this is important..
+
+# Normal Comment
+
+# This **shouldn't** be a comment..
+scoreboard players operation @s obj = #fakeplayer obj
+
+#> single line block comment
+tellraw @a "This string # has # hashtags o_O"
+
+---tokens---
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'Get:' Literal.String.Doc
+' ' Text.Whitespace
+'#rx.playerdb:api/v2/get' Name.Function
+'\n' Text
+
+'#>' Comment.Multiline
+'\n' Text
+
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'@input' Name.Decorator
+'\n' Text
+
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'$in.uid' Name.Variable.Magic
+' ' Text.Whitespace
+'rx.io' Literal.String.Doc
+'\n' Text
+
+'#>' Comment.Multiline
+'\n' Text
+
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'@output' Name.Decorator
+'\n' Text
+
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'rx.playerdb:io' Name.Function
+' ' Text.Whitespace
+'player' Literal.String.Doc
+'\n' Text
+
+'#' Comment.Multiline
+'\n' Text
+
+'#' Comment.Multiline
+' ' Text.Whitespace
+'Selects' Comment.Multiline
+' ' Text.Whitespace
+'data' Comment.Multiline
+' ' Text.Whitespace
+'inside' Comment.Multiline
+' ' Text.Whitespace
+'the' Comment.Multiline
+' ' Text.Whitespace
+'database' Comment.Multiline
+' ' Text.Whitespace
+'and' Comment.Multiline
+' ' Text.Whitespace
+'copies' Comment.Multiline
+' ' Text.Whitespace
+'to' Comment.Multiline
+' ' Text.Whitespace
+'rx.playerdb:io' Name.Function
+' ' Text.Whitespace
+'player' Comment.Multiline
+'\n' Text
+
+'#' Comment.Multiline
+' ' Text.Whitespace
+'See' Comment.Multiline
+' ' Text.Whitespace
+'#api/v2/select' Name.Function
+' ' Text.Whitespace
+'for' Comment.Multiline
+' ' Text.Whitespace
+'more' Comment.Multiline
+' ' Text.Whitespace
+'info..' Comment.Multiline
+'\n' Text
+
+'#' Comment.Multiline
+' \n' Text.Whitespace
+
+'#*' Comment.Multiline
+' ' Text.Whitespace
+'Note:' Comment.Multiline
+' ' Text.Whitespace
+'something,' Comment.Multiline
+' ' Text.Whitespace
+'something,' Comment.Multiline
+' ' Text.Whitespace
+'this' Comment.Multiline
+' ' Text.Whitespace
+'is' Comment.Multiline
+' ' Text.Whitespace
+'important..' Comment.Multiline
+'\n' Text
+
+'\n#' Comment.Multiline
+' ' Text.Whitespace
+'Normal' Comment.Multiline
+' ' Text.Whitespace
+'Comment' Comment.Multiline
+'\n' Text
+
+'\n#' Comment.Multiline
+' ' Text.Whitespace
+'This' Comment.Multiline
+' ' Text.Whitespace
+"**shouldn't**" Comment.Multiline
+' ' Text.Whitespace
+'be' Comment.Multiline
+' ' Text.Whitespace
+'a' Comment.Multiline
+' ' Text.Whitespace
+'comment..' Comment.Multiline
+'\n' Text
+
+'scoreboard' Name.Builtin
+' ' Text.Whitespace
+'players' Keyword.Constant
+' ' Text.Whitespace
+'operation' Keyword.Constant
+' ' Text.Whitespace
+'@s' Name.Variable
+' ' Text.Whitespace
+'obj' Keyword.Constant
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'#fakeplayer' Name.Variable.Magic
+' ' Text.Whitespace
+'obj' Keyword.Constant
+'\n\n' Text.Whitespace
+
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'single' Literal.String.Doc
+' ' Text.Whitespace
+'line' Literal.String.Doc
+' ' Text.Whitespace
+'block' Literal.String.Doc
+' ' Text.Whitespace
+'comment' Literal.String.Doc
+'\n' Text
+
+'tellraw' Name.Builtin
+' ' Text.Whitespace
+'@a' Name.Variable
+' ' Text.Whitespace
+'"' Literal.String.Double
+'This string # has # hashtags o_O' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/coordinates.txt b/tests/snippets/mcfunction/coordinates.txt
new file mode 100644
index 0000000..d5398bc
--- /dev/null
+++ b/tests/snippets/mcfunction/coordinates.txt
@@ -0,0 +1,188 @@
+---input---
+# normal
+tp 1 2 3
+tp 100.5 80 -100.5
+
+# relative
+tp 10 ~ -10
+tp 10 ~10 -10
+tp 10 ~0.5 -10
+tp 10 ~.5 -10
+tp 10 ~-10 -10
+tp 10 ~-0.5 -10
+tp 10 ~-.5 -10
+
+# carrot
+tp 10 ^ -10
+tp 10 ^10 -10
+tp 10 ^0.5 -10
+tp 10 ^.5 -10
+tp 10 ^-10 -10
+tp 10 ^-0.5 -10
+tp 10 ^-.5 -10
+
+---tokens---
+'# normal' Comment.Single
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'1' Literal.Number.Float
+' ' Text.Whitespace
+'2' Literal.Number.Float
+' ' Text.Whitespace
+'3' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'100.5' Literal.Number.Float
+' ' Text.Whitespace
+'80' Literal.Number.Float
+' ' Text.Whitespace
+'-100.5' Literal.Number.Float
+'\n\n' Text.Whitespace
+
+'# relative' Comment.Single
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'0.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'-10' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'-0.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'~' Operator
+'-.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n\n' Text.Whitespace
+
+'# carrot' Comment.Single
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'0.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'-10' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'-0.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'tp' Name.Builtin
+' ' Text.Whitespace
+'10' Literal.Number.Float
+' ' Text.Whitespace
+'^' Operator
+'-.5' Literal.Number.Float
+' ' Text.Whitespace
+'-10' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/data.txt b/tests/snippets/mcfunction/data.txt
new file mode 100644
index 0000000..440d668
--- /dev/null
+++ b/tests/snippets/mcfunction/data.txt
@@ -0,0 +1,120 @@
+---input---
+data modify storage my:storage root set value {
+ key: "This NBT Compound is multiple lines",
+ Count: 10b,
+ tags: [
+ 0,
+ 1,
+ ],
+ UUID
+}
+
+tellraw @a {
+ "text": "how ever",
+ "color": "blue",
+ "extra": [
+ "this is json o_O"
+ ]
+}
+
+---tokens---
+'data' Name.Builtin
+' ' Text.Whitespace
+'modify' Keyword.Constant
+' ' Text.Whitespace
+'storage' Keyword.Constant
+' ' Text.Whitespace
+'my:storage' Name.Function
+' ' Text.Whitespace
+'root' Keyword.Constant
+' ' Text.Whitespace
+'set' Keyword.Constant
+' ' Text.Whitespace
+'value' Keyword.Constant
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'key' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'This NBT Compound is multiple lines' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'Count' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'10' Literal.Number.Integer
+'b' Name.Attribute
+',' Punctuation
+'\n ' Text.Whitespace
+'tags' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'\n ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+'\n ' Text.Whitespace
+'1' Literal.Number.Integer
+',' Punctuation
+'\n ' Text.Whitespace
+']' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'UUID' Name.Attribute
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n\n' Text.Whitespace
+
+'tellraw' Name.Builtin
+' ' Text.Whitespace
+'@a' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'"' Name.Attribute
+'text' Name.Attribute
+'"' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'how ever' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'"' Name.Attribute
+'color' Name.Attribute
+'"' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'blue' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'"' Name.Attribute
+'extra' Name.Attribute
+'"' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'\n ' Text.Whitespace
+'"' Name.Attribute
+'this' Name.Attribute
+' ' Text.Whitespace
+'is' Name.Attribute
+' ' Text.Whitespace
+'json' Name.Attribute
+' ' Text.Whitespace
+'o_' Name.Attribute
+'O' Name.Attribute
+'"' Name.Attribute
+'\n ' Text.Whitespace
+']' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/difficult_1.txt b/tests/snippets/mcfunction/difficult_1.txt
new file mode 100644
index 0000000..f9ad1bc
--- /dev/null
+++ b/tests/snippets/mcfunction/difficult_1.txt
@@ -0,0 +1,56 @@
+---input---
+# execute as @e[nbt={ Item: {id: "minecraft:diamond", Count: 64 } }] run
+# setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]}
+# tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}]
+
+execute as @a[advancements={minecraft:story/form_obsidian={foo=true, bar=false},minecraft:story/follow_ender_eye={foo=false, bar=true}}] run
+
+---tokens---
+'# execute as @e[nbt={ Item: {id: "minecraft:diamond", Count: 64 } }] run' Comment.Single
+'\n' Text.Whitespace
+
+'# setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]}' Comment.Single
+'\n' Text.Whitespace
+
+'# tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}]' Comment.Single
+'\n\n' Text.Whitespace
+
+'execute' Name.Builtin
+' ' Text.Whitespace
+'as' Keyword.Constant
+' ' Text.Whitespace
+'@a' Name.Variable
+'[' Punctuation
+'advancements' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'minecraft:story/form_obsidian' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'foo' Name.Attribute
+'=' Punctuation
+'true' Name.Tag
+',' Punctuation
+' ' Text.Whitespace
+'bar' Name.Attribute
+'=' Punctuation
+'false' Name.Tag
+'}' Punctuation
+',' Punctuation
+'minecraft:story/follow_ender_eye' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'foo' Name.Attribute
+'=' Punctuation
+'false' Name.Tag
+',' Punctuation
+' ' Text.Whitespace
+'bar' Name.Attribute
+'=' Punctuation
+'true' Name.Tag
+'}' Punctuation
+'}' Punctuation
+']' Punctuation
+' ' Text.Whitespace
+'run' Keyword.Constant
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/multiline.txt b/tests/snippets/mcfunction/multiline.txt
new file mode 100644
index 0000000..1587910
--- /dev/null
+++ b/tests/snippets/mcfunction/multiline.txt
@@ -0,0 +1,108 @@
+---input---
+execute
+ as @a # For each "player",
+ at @s # start at their feet.
+ anchored eyes # Looking through their eyes,
+ facing 0 0 0 # face perfectly at the target
+ anchored feet # (go back to the feet)
+ positioned ^ ^ ^1 # and move one block forward.
+ rotated as @s # Face the direction the player
+ # is actually facing,
+ positioned ^ ^ ^-1 # and move one block back.
+ if entity @s[distance=..0.6] # Check if we're close to the
+ # player's feet.
+ run
+ say "I'm facing the target!"
+
+---tokens---
+'execute' Name.Builtin
+'\n ' Text.Whitespace
+'as' Keyword.Constant
+' ' Text.Whitespace
+'@a' Name.Variable
+' ' Text.Whitespace
+'# For each "player",' Comment.Single
+'\n ' Text.Whitespace
+'at' Keyword.Constant
+' ' Text.Whitespace
+'@s' Name.Variable
+' ' Text.Whitespace
+'# start at their feet.' Comment.Single
+'\n ' Text.Whitespace
+'anchored' Keyword.Constant
+' ' Text.Whitespace
+'eyes' Keyword.Constant
+' ' Text.Whitespace
+'# Looking through their eyes,' Comment.Single
+'\n ' Text.Whitespace
+'facing' Keyword.Constant
+' ' Text.Whitespace
+'0' Literal.Number.Float
+' ' Text.Whitespace
+'0' Literal.Number.Float
+' ' Text.Whitespace
+'0' Literal.Number.Float
+' ' Text.Whitespace
+'# face perfectly at the target' Comment.Single
+'\n ' Text.Whitespace
+'anchored' Keyword.Constant
+' ' Text.Whitespace
+'feet' Keyword.Constant
+' ' Text.Whitespace
+'# (go back to the feet)' Comment.Single
+'\n ' Text.Whitespace
+'positioned' Keyword.Constant
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'^' Operator
+'1' Literal.Number.Float
+' ' Text.Whitespace
+'# and move one block forward.' Comment.Single
+'\n ' Text.Whitespace
+'rotated' Keyword.Constant
+' ' Text.Whitespace
+'as' Keyword.Constant
+' ' Text.Whitespace
+'@s' Name.Variable
+' ' Text.Whitespace
+'# Face the direction the player' Comment.Single
+'\n ' Text.Whitespace
+'# is actually facing,' Comment.Single
+'\n ' Text.Whitespace
+'positioned' Keyword.Constant
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'^' Operator
+'-1' Literal.Number.Float
+' ' Text.Whitespace
+'# and move one block back.' Comment.Single
+'\n ' Text.Whitespace
+'if' Keyword.Constant
+' ' Text.Whitespace
+'entity' Keyword.Constant
+' ' Text.Whitespace
+'@s' Name.Variable
+'[' Punctuation
+'distance' Name.Attribute
+'=' Punctuation
+'..' Literal
+'0.6' Literal.Number.Float
+']' Punctuation
+' ' Text.Whitespace
+"# Check if we're close to the" Comment.Single
+'\n ' Text.Whitespace
+"# player's feet." Comment.Single
+'\n ' Text.Whitespace
+'run' Keyword.Constant
+' \n say' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String.Double
+"I'm facing the target!" Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/selectors.txt b/tests/snippets/mcfunction/selectors.txt
new file mode 100644
index 0000000..ea69c78
--- /dev/null
+++ b/tests/snippets/mcfunction/selectors.txt
@@ -0,0 +1,73 @@
+---input---
+execute
+ as @a[
+ advancements={
+ minecraft:story/form_obsidian={
+ foo=true,
+ bar=false
+ },
+ minecraft:story/follow_ender_eye={
+ foo=false, bar=true
+ }
+ }
+ ] run
+ say this command is nuts
+
+---tokens---
+'execute' Name.Builtin
+'\n ' Text.Whitespace
+'as' Keyword.Constant
+' ' Text.Whitespace
+'@a' Name.Variable
+'[' Punctuation
+'\n ' Text.Whitespace
+'advancements' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'\n ' Text.Whitespace
+'minecraft:story/form_obsidian' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'\n ' Text.Whitespace
+'foo' Name.Attribute
+'=' Punctuation
+'true' Name.Tag
+',' Punctuation
+'\n ' Text.Whitespace
+'bar' Name.Attribute
+'=' Punctuation
+'false' Name.Tag
+'\n ' Text.Whitespace
+'}' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'minecraft:story/follow_ender_eye' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'\n ' Text.Whitespace
+'foo' Name.Attribute
+'=' Punctuation
+'false' Name.Tag
+',' Punctuation
+' ' Text.Whitespace
+'bar' Name.Attribute
+'=' Punctuation
+'true' Name.Tag
+'\n ' Text.Whitespace
+'}' Punctuation
+'\n ' Text.Whitespace
+'}' Punctuation
+'\n ' Text.Whitespace
+']' Punctuation
+' ' Text.Whitespace
+'run' Keyword.Constant
+'\n say' Name.Builtin
+' ' Text.Whitespace
+'this' Keyword.Constant
+' ' Text.Whitespace
+'command' Keyword.Constant
+' ' Text.Whitespace
+'is' Keyword.Constant
+' ' Text.Whitespace
+'nuts' Keyword.Constant
+'\n' Text.Whitespace
diff --git a/tests/snippets/mcfunction/simple.txt b/tests/snippets/mcfunction/simple.txt
new file mode 100644
index 0000000..664c91c
--- /dev/null
+++ b/tests/snippets/mcfunction/simple.txt
@@ -0,0 +1,92 @@
+---input---
+#> This command looks for a player with 10 hp and prints a message
+# @param - @s = player
+
+execute as @a[name="rx", nbt={Health: 10.0f}] run tellraw @a {"text": "this is my cool command"} # epic
+
+---tokens---
+'#>' Comment.Multiline
+' ' Text.Whitespace
+'This' Literal.String.Doc
+' ' Text.Whitespace
+'command' Literal.String.Doc
+' ' Text.Whitespace
+'looks' Literal.String.Doc
+' ' Text.Whitespace
+'for' Literal.String.Doc
+' ' Text.Whitespace
+'a' Literal.String.Doc
+' ' Text.Whitespace
+'player' Literal.String.Doc
+' ' Text.Whitespace
+'with' Literal.String.Doc
+' ' Text.Whitespace
+'10' Literal.String.Doc
+' ' Text.Whitespace
+'hp' Literal.String.Doc
+' ' Text.Whitespace
+'and' Literal.String.Doc
+' ' Text.Whitespace
+'prints' Literal.String.Doc
+' ' Text.Whitespace
+'a' Literal.String.Doc
+' ' Text.Whitespace
+'message' Literal.String.Doc
+'\n' Text
+
+'#' Comment.Multiline
+' ' Text.Whitespace
+'@param' Name.Decorator
+' ' Text.Whitespace
+'-' Comment.Multiline
+' ' Text.Whitespace
+'@s' Name.Decorator
+' ' Text.Whitespace
+'=' Comment.Multiline
+' ' Text.Whitespace
+'player' Comment.Multiline
+'\n' Text
+
+'\n' Text.Whitespace
+
+'execute' Name.Builtin
+' ' Text.Whitespace
+'as' Keyword.Constant
+' ' Text.Whitespace
+'@a' Name.Variable
+'[' Punctuation
+'name' Name.Attribute
+'=' Punctuation
+'"' Literal.String.Double
+'rx' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+' ' Text.Whitespace
+'nbt' Name.Attribute
+'=' Punctuation
+'{' Punctuation
+'Health' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'10.0f' Literal.Number.Float
+'}' Punctuation
+']' Punctuation
+' ' Text.Whitespace
+'run' Keyword.Constant
+' tellraw' Name.Builtin
+' ' Text.Whitespace
+'@a' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'"' Name.Attribute
+'text' Name.Attribute
+'"' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'this is my cool command' Literal.String.Double
+'"' Literal.String.Double
+'}' Punctuation
+' ' Text.Whitespace
+'# epic' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bold_fenced_by_asterisk.txt b/tests/snippets/md/test_bold_fenced_by_asterisk.txt
new file mode 100644
index 0000000..25c899d
--- /dev/null
+++ b/tests/snippets/md/test_bold_fenced_by_asterisk.txt
@@ -0,0 +1,15 @@
+---input---
+**bold**
+
+(**bold**)
+
+---tokens---
+'**bold**' Generic.Strong
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'(' Text
+'**bold**' Generic.Strong
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bold_fenced_by_underscore.txt b/tests/snippets/md/test_bold_fenced_by_underscore.txt
new file mode 100644
index 0000000..49c9110
--- /dev/null
+++ b/tests/snippets/md/test_bold_fenced_by_underscore.txt
@@ -0,0 +1,15 @@
+---input---
+__bold__
+
+(__bold__)
+
+---tokens---
+'__bold__' Generic.Strong
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'(' Text
+'__bold__' Generic.Strong
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bulleted_list_1.txt b/tests/snippets/md/test_bulleted_list_1.txt
new file mode 100644
index 0000000..8da212c
--- /dev/null
+++ b/tests/snippets/md/test_bulleted_list_1.txt
@@ -0,0 +1,14 @@
+---input---
+* foo
+* bar
+
+---tokens---
+'*' Keyword
+' ' Text.Whitespace
+'foo' Text
+'\n' Text.Whitespace
+
+'*' Keyword
+' ' Text.Whitespace
+'bar' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bulleted_list_2.txt b/tests/snippets/md/test_bulleted_list_2.txt
new file mode 100644
index 0000000..040b908
--- /dev/null
+++ b/tests/snippets/md/test_bulleted_list_2.txt
@@ -0,0 +1,14 @@
+---input---
+- foo
+- bar
+
+---tokens---
+'-' Keyword
+' ' Text.Whitespace
+'foo' Text
+'\n' Text.Whitespace
+
+'-' Keyword
+' ' Text.Whitespace
+'bar' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bulleted_list_3.txt b/tests/snippets/md/test_bulleted_list_3.txt
new file mode 100644
index 0000000..a59d5f7
--- /dev/null
+++ b/tests/snippets/md/test_bulleted_list_3.txt
@@ -0,0 +1,14 @@
+---input---
+* *foo*
+* bar
+
+---tokens---
+'*' Keyword
+' ' Text.Whitespace
+'*foo*' Generic.Emph
+'\n' Text.Whitespace
+
+'*' Keyword
+' ' Text.Whitespace
+'bar' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_bulleted_list_4.txt b/tests/snippets/md/test_bulleted_list_4.txt
new file mode 100644
index 0000000..111f9cf
--- /dev/null
+++ b/tests/snippets/md/test_bulleted_list_4.txt
@@ -0,0 +1,19 @@
+---input---
+```
+code
+```
+* *foo*
+* bar
+
+---tokens---
+'```\ncode\n```\n' Literal.String.Backtick
+
+'*' Keyword
+' ' Text.Whitespace
+'*foo*' Generic.Emph
+'\n' Text.Whitespace
+
+'*' Keyword
+' ' Text.Whitespace
+'bar' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_code_block_fenced_by_backticks.txt b/tests/snippets/md/test_code_block_fenced_by_backticks.txt
new file mode 100644
index 0000000..4f8fefa
--- /dev/null
+++ b/tests/snippets/md/test_code_block_fenced_by_backticks.txt
@@ -0,0 +1,15 @@
+---input---
+```
+code
+```
+
+```
+multi
+`line`
+code
+```
+
+---tokens---
+'```\ncode\n```\n' Literal.String.Backtick
+
+'\n```\nmulti\n`line`\ncode\n```\n' Literal.String.Backtick
diff --git a/tests/snippets/md/test_code_block_with_language.txt b/tests/snippets/md/test_code_block_with_language.txt
new file mode 100644
index 0000000..41f2563
--- /dev/null
+++ b/tests/snippets/md/test_code_block_with_language.txt
@@ -0,0 +1,16 @@
+---input---
+```python
+import this
+```
+
+---tokens---
+'```' Literal.String.Backtick
+'python' Literal.String.Backtick
+'\n' Text
+
+'import' Keyword.Namespace
+' ' Text
+'this' Name.Namespace
+'\n' Text.Whitespace
+
+'```\n' Literal.String.Backtick
diff --git a/tests/snippets/md/test_escape_italics.txt b/tests/snippets/md/test_escape_italics.txt
new file mode 100644
index 0000000..42e1cb4
--- /dev/null
+++ b/tests/snippets/md/test_escape_italics.txt
@@ -0,0 +1,23 @@
+---input---
+\*no italics\*
+
+\_ no italics \_
+
+---tokens---
+'\\*' Text
+'no' Text
+' ' Text
+'italics' Text
+'\\*' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'\\_' Text
+' ' Text
+'no' Text
+' ' Text
+'italics' Text
+' ' Text
+'\\_' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_inline_code.txt b/tests/snippets/md/test_inline_code.txt
new file mode 100644
index 0000000..c9a3964
--- /dev/null
+++ b/tests/snippets/md/test_inline_code.txt
@@ -0,0 +1,36 @@
+---input---
+code: `code`
+
+ `**code**`
+
+(`code`)
+
+code (`in brackets`)
+
+---tokens---
+'code:' Text
+' ' Text
+'`code`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text
+'`**code**`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'(' Text
+'`code`' Literal.String.Backtick
+')' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'code' Text
+' ' Text
+'(' Text
+'`in brackets`' Literal.String.Backtick
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_inline_code_after_block.txt b/tests/snippets/md/test_inline_code_after_block.txt
new file mode 100644
index 0000000..e1f704b
--- /dev/null
+++ b/tests/snippets/md/test_inline_code_after_block.txt
@@ -0,0 +1,19 @@
+---input---
+```
+code
+```
+* nocode
+* `code`
+
+---tokens---
+'```\ncode\n```\n' Literal.String.Backtick
+
+'*' Keyword
+' ' Text.Whitespace
+'nocode' Text
+'\n' Text.Whitespace
+
+'*' Keyword
+' ' Text.Whitespace
+'`code`' Literal.String.Backtick
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_inline_code_in_list.txt b/tests/snippets/md/test_inline_code_in_list.txt
new file mode 100644
index 0000000..9cd8aa0
--- /dev/null
+++ b/tests/snippets/md/test_inline_code_in_list.txt
@@ -0,0 +1,26 @@
+---input---
+* `code`
+
+- `code`
+
+1. `code`
+
+---tokens---
+'*' Keyword
+' ' Text.Whitespace
+'`code`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'-' Keyword
+' ' Text.Whitespace
+'`code`' Literal.String.Backtick
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'1.' Keyword
+' ' Text
+'`code`' Literal.String.Backtick
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_invalid_bold.txt b/tests/snippets/md/test_invalid_bold.txt
new file mode 100644
index 0000000..6a5db6a
--- /dev/null
+++ b/tests/snippets/md/test_invalid_bold.txt
@@ -0,0 +1,31 @@
+---input---
+**no bold__
+
+__no bold**
+
+*no bold*
+
+_no bold_
+
+---tokens---
+'**no' Text
+' ' Text
+'bold__' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'__no' Text
+' ' Text
+'bold**' Text
+'\n' Text.Whitespace
+
+'\n' Text
+
+'*no bold*' Generic.Emph
+'\n' Text.Whitespace
+
+'\n' Text
+
+'_no bold_' Generic.Emph
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_invalid_italics.txt b/tests/snippets/md/test_invalid_italics.txt
new file mode 100644
index 0000000..28acb5d
--- /dev/null
+++ b/tests/snippets/md/test_invalid_italics.txt
@@ -0,0 +1,31 @@
+---input---
+*no italics_
+
+_no italics*
+
+**no italics**
+
+__no italics__
+
+---tokens---
+'*no' Text
+' ' Text
+'italics_' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'_no' Text
+' ' Text
+'italics*' Text
+'\n' Text.Whitespace
+
+'\n' Text
+
+'**no italics**' Generic.Strong
+'\n' Text.Whitespace
+
+'\n' Text
+
+'__no italics__' Generic.Strong
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_italics_and_bold.txt b/tests/snippets/md/test_italics_and_bold.txt
new file mode 100644
index 0000000..bb2bf4f
--- /dev/null
+++ b/tests/snippets/md/test_italics_and_bold.txt
@@ -0,0 +1,21 @@
+---input---
+**bold** and *italics*
+
+*italics* and **bold**
+
+---tokens---
+'**bold**' Generic.Strong
+' ' Text
+'and' Text
+' ' Text
+'*italics*' Generic.Emph
+'\n' Text.Whitespace
+
+'\n' Text
+
+'*italics*' Generic.Emph
+' ' Text
+'and' Text
+' ' Text
+'**bold**' Generic.Strong
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_italics_fenced_by_asterisk.txt b/tests/snippets/md/test_italics_fenced_by_asterisk.txt
new file mode 100644
index 0000000..cd8775e
--- /dev/null
+++ b/tests/snippets/md/test_italics_fenced_by_asterisk.txt
@@ -0,0 +1,15 @@
+---input---
+*italics*
+
+(*italics*)
+
+---tokens---
+'*italics*' Generic.Emph
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'(' Text
+'*italics*' Generic.Emph
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_italics_fenced_by_underscore.txt b/tests/snippets/md/test_italics_fenced_by_underscore.txt
new file mode 100644
index 0000000..5f57756
--- /dev/null
+++ b/tests/snippets/md/test_italics_fenced_by_underscore.txt
@@ -0,0 +1,15 @@
+---input---
+_italics_
+
+(_italics_)
+
+---tokens---
+'_italics_' Generic.Emph
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'(' Text
+'_italics_' Generic.Emph
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_italics_no_multiline.txt b/tests/snippets/md/test_italics_no_multiline.txt
new file mode 100644
index 0000000..2657ed1
--- /dev/null
+++ b/tests/snippets/md/test_italics_no_multiline.txt
@@ -0,0 +1,10 @@
+---input---
+*no
+italics*
+
+---tokens---
+'*no' Text
+'\n' Text.Whitespace
+
+'italics*' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_links.txt b/tests/snippets/md/test_links.txt
new file mode 100644
index 0000000..9fc46e8
--- /dev/null
+++ b/tests/snippets/md/test_links.txt
@@ -0,0 +1,23 @@
+---input---
+[text](link)
+
+![Image of foo](https://bar.baz)
+
+---tokens---
+'[' Text
+'text' Name.Tag
+']' Text
+'(' Text
+'link' Name.Attribute
+')' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'![' Text
+'Image of foo' Name.Tag
+']' Text
+'(' Text
+'https://bar.baz' Name.Attribute
+')' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_mentions.txt b/tests/snippets/md/test_mentions.txt
new file mode 100644
index 0000000..a2def5c
--- /dev/null
+++ b/tests/snippets/md/test_mentions.txt
@@ -0,0 +1,10 @@
+---input---
+note for @me:
+
+---tokens---
+'note' Text
+' ' Text
+'for' Text
+' ' Text
+'@me:' Name.Entity
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_numbered_list.txt b/tests/snippets/md/test_numbered_list.txt
new file mode 100644
index 0000000..1384d6b
--- /dev/null
+++ b/tests/snippets/md/test_numbered_list.txt
@@ -0,0 +1,14 @@
+---input---
+1. foo
+2. bar
+
+---tokens---
+'1.' Keyword
+' ' Text
+'foo' Text
+'\n' Text.Whitespace
+
+'2.' Keyword
+' ' Text
+'bar' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_quote.txt b/tests/snippets/md/test_quote.txt
new file mode 100644
index 0000000..cc64ec2
--- /dev/null
+++ b/tests/snippets/md/test_quote.txt
@@ -0,0 +1,10 @@
+---input---
+> a
+> quote
+
+---tokens---
+'> ' Keyword
+'a\n' Generic.Emph
+
+'> ' Keyword
+'quote\n' Generic.Emph
diff --git a/tests/snippets/md/test_reference_style_links.txt b/tests/snippets/md/test_reference_style_links.txt
new file mode 100644
index 0000000..813cef3
--- /dev/null
+++ b/tests/snippets/md/test_reference_style_links.txt
@@ -0,0 +1,18 @@
+---input---
+[an example][id]
+[id]: http://example.com
+
+---tokens---
+'[' Text
+'an example' Name.Tag
+']' Text
+'[' Text
+'id' Name.Label
+']' Text
+'\n' Text.Whitespace
+
+'[' Text
+'id' Name.Label
+']: ' Text
+'http://example.com' Name.Attribute
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_strikethrough.txt b/tests/snippets/md/test_strikethrough.txt
new file mode 100644
index 0000000..483c63a
--- /dev/null
+++ b/tests/snippets/md/test_strikethrough.txt
@@ -0,0 +1,9 @@
+---input---
+~~striked~~not striked
+
+---tokens---
+'~~striked~~' Generic.Deleted
+'not' Text
+' ' Text
+'striked' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_task_list.txt b/tests/snippets/md/test_task_list.txt
new file mode 100644
index 0000000..7a64506
--- /dev/null
+++ b/tests/snippets/md/test_task_list.txt
@@ -0,0 +1,34 @@
+---input---
+- [ ] sample task
+
+* [ ] sample task
+
+ * [ ] sample task
+
+---tokens---
+'- ' Keyword
+'[ ]' Keyword
+' ' Text
+'sample' Text
+' ' Text
+'task' Text
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'* ' Keyword
+'[ ]' Keyword
+' ' Text
+'sample' Text
+' ' Text
+'task' Text
+'\n' Text.Whitespace
+
+'\n ' Text.Whitespace
+'* ' Keyword
+'[ ]' Keyword
+' ' Text
+'sample' Text
+' ' Text
+'task' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/md/test_topics.txt b/tests/snippets/md/test_topics.txt
new file mode 100644
index 0000000..18029cc
--- /dev/null
+++ b/tests/snippets/md/test_topics.txt
@@ -0,0 +1,10 @@
+---input---
+message to #you:
+
+---tokens---
+'message' Text
+' ' Text
+'to' Text
+' ' Text
+'#you:' Name.Entity
+'\n' Text.Whitespace
diff --git a/tests/snippets/mips/deprecated_substrings.txt b/tests/snippets/mips/deprecated_substrings.txt
new file mode 100644
index 0000000..4e7aa7d
--- /dev/null
+++ b/tests/snippets/mips/deprecated_substrings.txt
@@ -0,0 +1,34 @@
+---input---
+beql
+bnel
+bgtzl
+bgezl
+bltzl
+blezl
+bltzall
+bgezall
+
+---tokens---
+'beql' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bnel' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bgtzl' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bgezl' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bltzl' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'blezl' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bltzall' Keyword.Pseudo
+'\n' Text.Whitespace
+
+'bgezall' Keyword.Pseudo
+'\n' Text.Whitespace
diff --git a/tests/snippets/mips/keyword_substrings.txt b/tests/snippets/mips/keyword_substrings.txt
new file mode 100644
index 0000000..70d68d5
--- /dev/null
+++ b/tests/snippets/mips/keyword_substrings.txt
@@ -0,0 +1,254 @@
+---input---
+subu
+subi
+sub
+addu
+addiu
+addi
+add
+multu
+mult
+mulu
+mul
+maddu
+madd
+msubu
+msub
+divu
+div
+nor
+xor
+andi
+and
+ori
+xori
+or
+sllv
+sll
+srlv
+srl
+srav
+sra
+sltiu
+sltu
+slti
+slt
+jalr
+jal
+jr
+j
+bgezal
+bgez
+bltzal
+bltz
+lbu
+lb
+lhu
+lh
+lwr
+lw
+swl
+swr
+sw
+teqi
+teq
+tneqi
+tne
+tgeiu
+tgeu
+tgei
+tge
+tltiu
+tltu
+tlti
+tlt
+
+---tokens---
+'subu' Keyword
+'\n' Text.Whitespace
+
+'subi' Keyword
+'\n' Text.Whitespace
+
+'sub' Keyword
+'\n' Text.Whitespace
+
+'addu' Keyword
+'\n' Text.Whitespace
+
+'addiu' Keyword
+'\n' Text.Whitespace
+
+'addi' Keyword
+'\n' Text.Whitespace
+
+'add' Keyword
+'\n' Text.Whitespace
+
+'multu' Keyword
+'\n' Text.Whitespace
+
+'mult' Keyword
+'\n' Text.Whitespace
+
+'mulu' Keyword
+'\n' Text.Whitespace
+
+'mul' Keyword
+'\n' Text.Whitespace
+
+'maddu' Keyword
+'\n' Text.Whitespace
+
+'madd' Keyword
+'\n' Text.Whitespace
+
+'msubu' Keyword
+'\n' Text.Whitespace
+
+'msub' Keyword
+'\n' Text.Whitespace
+
+'divu' Keyword
+'\n' Text.Whitespace
+
+'div' Keyword
+'\n' Text.Whitespace
+
+'nor' Keyword
+'\n' Text.Whitespace
+
+'xor' Keyword
+'\n' Text.Whitespace
+
+'andi' Keyword
+'\n' Text.Whitespace
+
+'and' Keyword
+'\n' Text.Whitespace
+
+'ori' Keyword
+'\n' Text.Whitespace
+
+'xori' Keyword
+'\n' Text.Whitespace
+
+'or' Keyword
+'\n' Text.Whitespace
+
+'sllv' Keyword
+'\n' Text.Whitespace
+
+'sll' Keyword
+'\n' Text.Whitespace
+
+'srlv' Keyword
+'\n' Text.Whitespace
+
+'srl' Keyword
+'\n' Text.Whitespace
+
+'srav' Keyword
+'\n' Text.Whitespace
+
+'sra' Keyword
+'\n' Text.Whitespace
+
+'sltiu' Keyword
+'\n' Text.Whitespace
+
+'sltu' Keyword
+'\n' Text.Whitespace
+
+'slti' Keyword
+'\n' Text.Whitespace
+
+'slt' Keyword
+'\n' Text.Whitespace
+
+'jalr' Keyword
+'\n' Text.Whitespace
+
+'jal' Keyword
+'\n' Text.Whitespace
+
+'jr' Keyword
+'\n' Text.Whitespace
+
+'j' Keyword
+'\n' Text.Whitespace
+
+'bgezal' Keyword
+'\n' Text.Whitespace
+
+'bgez' Keyword
+'\n' Text.Whitespace
+
+'bltzal' Keyword
+'\n' Text.Whitespace
+
+'bltz' Keyword
+'\n' Text.Whitespace
+
+'lbu' Keyword
+'\n' Text.Whitespace
+
+'lb' Keyword
+'\n' Text.Whitespace
+
+'lhu' Keyword
+'\n' Text.Whitespace
+
+'lh' Keyword
+'\n' Text.Whitespace
+
+'lwr' Keyword
+'\n' Text.Whitespace
+
+'lw' Keyword
+'\n' Text.Whitespace
+
+'swl' Keyword
+'\n' Text.Whitespace
+
+'swr' Keyword
+'\n' Text.Whitespace
+
+'sw' Keyword
+'\n' Text.Whitespace
+
+'teqi' Keyword
+'\n' Text.Whitespace
+
+'teq' Keyword
+'\n' Text.Whitespace
+
+'tneqi' Keyword
+'\n' Text.Whitespace
+
+'tne' Keyword
+'\n' Text.Whitespace
+
+'tgeiu' Keyword
+'\n' Text.Whitespace
+
+'tgeu' Keyword
+'\n' Text.Whitespace
+
+'tgei' Keyword
+'\n' Text.Whitespace
+
+'tge' Keyword
+'\n' Text.Whitespace
+
+'tltiu' Keyword
+'\n' Text.Whitespace
+
+'tltu' Keyword
+'\n' Text.Whitespace
+
+'tlti' Keyword
+'\n' Text.Whitespace
+
+'tlt' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/mips/variable_substrings.txt b/tests/snippets/mips/variable_substrings.txt
new file mode 100644
index 0000000..85e845d
--- /dev/null
+++ b/tests/snippets/mips/variable_substrings.txt
@@ -0,0 +1,102 @@
+---input---
+remu
+rem
+mulou
+mulo
+negu
+neg
+beqz
+bgeu
+bge
+bgtu
+bgt
+bleu
+ble
+bltu
+blt
+bnez
+b
+ulhu
+ulh
+sgtu
+sgt
+sgeu
+sge
+sleu
+sle
+
+---tokens---
+'remu' Name.Variable
+'\n' Text.Whitespace
+
+'rem' Name.Variable
+'\n' Text.Whitespace
+
+'mulou' Name.Variable
+'\n' Text.Whitespace
+
+'mulo' Name.Variable
+'\n' Text.Whitespace
+
+'negu' Name.Variable
+'\n' Text.Whitespace
+
+'neg' Name.Variable
+'\n' Text.Whitespace
+
+'beqz' Name.Variable
+'\n' Text.Whitespace
+
+'bgeu' Name.Variable
+'\n' Text.Whitespace
+
+'bge' Name.Variable
+'\n' Text.Whitespace
+
+'bgtu' Name.Variable
+'\n' Text.Whitespace
+
+'bgt' Name.Variable
+'\n' Text.Whitespace
+
+'bleu' Name.Variable
+'\n' Text.Whitespace
+
+'ble' Name.Variable
+'\n' Text.Whitespace
+
+'bltu' Name.Variable
+'\n' Text.Whitespace
+
+'blt' Name.Variable
+'\n' Text.Whitespace
+
+'bnez' Name.Variable
+'\n' Text.Whitespace
+
+'b' Name.Variable
+'\n' Text.Whitespace
+
+'ulhu' Name.Variable
+'\n' Text.Whitespace
+
+'ulh' Name.Variable
+'\n' Text.Whitespace
+
+'sgtu' Name.Variable
+'\n' Text.Whitespace
+
+'sgt' Name.Variable
+'\n' Text.Whitespace
+
+'sgeu' Name.Variable
+'\n' Text.Whitespace
+
+'sge' Name.Variable
+'\n' Text.Whitespace
+
+'sleu' Name.Variable
+'\n' Text.Whitespace
+
+'sle' Name.Variable
+'\n' Text.Whitespace
diff --git a/tests/snippets/nasm/checkid.txt b/tests/snippets/nasm/checkid.txt
new file mode 100644
index 0000000..72f25da
--- /dev/null
+++ b/tests/snippets/nasm/checkid.txt
@@ -0,0 +1,32 @@
+---input---
+print_brick_no_color:
+ inc bx
+ mov di, bx ; comment
+ jmp check_col
+
+---tokens---
+'print_brick_no_color:' Name.Label
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'inc' Name.Function
+' ' Text.Whitespace
+'bx' Name.Builtin
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'mov' Name.Function
+' ' Text.Whitespace
+'di' Name.Builtin
+',' Punctuation
+' ' Text.Whitespace
+'bx' Name.Builtin
+' ' Text.Whitespace
+'; comment' Comment.Single
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'jmp' Name.Function
+' ' Text.Whitespace
+'check_col' Name.Variable
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_literal_number_bool.txt b/tests/snippets/objectivec/test_literal_number_bool.txt
new file mode 100644
index 0000000..4d6f965
--- /dev/null
+++ b/tests/snippets/objectivec/test_literal_number_bool.txt
@@ -0,0 +1,7 @@
+---input---
+@NO;
+
+---tokens---
+'@NO' Literal.Number
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_literal_number_bool_expression.txt b/tests/snippets/objectivec/test_literal_number_bool_expression.txt
new file mode 100644
index 0000000..36eb736
--- /dev/null
+++ b/tests/snippets/objectivec/test_literal_number_bool_expression.txt
@@ -0,0 +1,9 @@
+---input---
+@(YES);
+
+---tokens---
+'@(' Literal
+'YES' Name.Builtin
+')' Literal
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_literal_number_expression.txt b/tests/snippets/objectivec/test_literal_number_expression.txt
new file mode 100644
index 0000000..4ba2348
--- /dev/null
+++ b/tests/snippets/objectivec/test_literal_number_expression.txt
@@ -0,0 +1,11 @@
+---input---
+@(1+2);
+
+---tokens---
+'@(' Literal
+'1' Literal.Number.Integer
+'+' Operator
+'2' Literal.Number.Integer
+')' Literal
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_literal_number_int.txt b/tests/snippets/objectivec/test_literal_number_int.txt
new file mode 100644
index 0000000..35d947e
--- /dev/null
+++ b/tests/snippets/objectivec/test_literal_number_int.txt
@@ -0,0 +1,9 @@
+---input---
+@(1);
+
+---tokens---
+'@(' Literal
+'1' Literal.Number.Integer
+')' Literal
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_literal_number_nested_expression.txt b/tests/snippets/objectivec/test_literal_number_nested_expression.txt
new file mode 100644
index 0000000..b58d37c
--- /dev/null
+++ b/tests/snippets/objectivec/test_literal_number_nested_expression.txt
@@ -0,0 +1,15 @@
+---input---
+@(1+(2+3));
+
+---tokens---
+'@(' Literal
+'1' Literal.Number.Integer
+'+' Operator
+'(' Punctuation
+'2' Literal.Number.Integer
+'+' Operator
+'3' Literal.Number.Integer
+')' Punctuation
+')' Literal
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/objectivec/test_module_import.txt b/tests/snippets/objectivec/test_module_import.txt
new file mode 100644
index 0000000..c66e1c8
--- /dev/null
+++ b/tests/snippets/objectivec/test_module_import.txt
@@ -0,0 +1,9 @@
+---input---
+@import ModuleA;
+
+---tokens---
+'@import' Keyword
+' ' Text.Whitespace
+'ModuleA' Name
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/octave/test_multilinecomment.txt b/tests/snippets/octave/test_multilinecomment.txt
new file mode 100644
index 0000000..0987f4e
--- /dev/null
+++ b/tests/snippets/octave/test_multilinecomment.txt
@@ -0,0 +1,27 @@
+---input---
+%{
+This is a long comment
+ %}
+ #{
+This too
+#}
+This isnt
+
+---tokens---
+'%{\n' Comment.Multiline
+
+'This is a long comment\n' Comment.Multiline
+
+' %}' Comment.Multiline
+'\n ' Text
+'#{\n' Comment.Multiline
+
+'This too\n' Comment.Multiline
+
+'#}' Comment.Multiline
+'\n' Text
+
+'This' Name
+' ' Text
+'isnt' Name
+'\n' Text
diff --git a/tests/snippets/omg-idl/annotation_named_params.txt b/tests/snippets/omg-idl/annotation_named_params.txt
new file mode 100644
index 0000000..2758429
--- /dev/null
+++ b/tests/snippets/omg-idl/annotation_named_params.txt
@@ -0,0 +1,27 @@
+Asserts that annotation named parameters use Name, which is different from the
+normal "scoped_name =" lexing, which uses Name.Constant.
+
+---input---
+@mod::anno(value = const_a) const short const_b = const_a;
+
+---tokens---
+'@mod::anno' Name.Decorator
+'(' Punctuation
+'value' Name
+' ' Text.Whitespace
+'=' Punctuation
+' ' Text.Whitespace
+'const_a' Name
+')' Punctuation
+' ' Text.Whitespace
+'const' Keyword.Declaration
+' ' Text.Whitespace
+'short' Keyword.Type
+' ' Text.Whitespace
+'const_b' Name.Constant
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'const_a' Name
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/omg-idl/enumerators.txt b/tests/snippets/omg-idl/enumerators.txt
new file mode 100644
index 0000000..e6ff1e3
--- /dev/null
+++ b/tests/snippets/omg-idl/enumerators.txt
@@ -0,0 +1,18 @@
+Asserts that enumerators use Name.Constant instead of just Name.
+
+---input---
+enum Enum_t {enum_a, enum_b};
+
+---tokens---
+'enum' Keyword
+' ' Text.Whitespace
+'Enum_t' Name.Class
+' ' Text.Whitespace
+'{' Punctuation
+'enum_a' Name.Constant
+',' Punctuation
+' ' Text.Whitespace
+'enum_b' Name.Constant
+'}' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/peg/test_basic.txt b/tests/snippets/peg/test_basic.txt
new file mode 100644
index 0000000..13a9bb4
--- /dev/null
+++ b/tests/snippets/peg/test_basic.txt
@@ -0,0 +1,17 @@
+---input---
+rule<-("terminal"/nonterminal/[cls])*
+
+---tokens---
+'rule' Name.Class
+'<-' Operator
+'(' Punctuation
+'"terminal"' Literal.String.Double
+'/' Operator
+'nonterminal' Name.Class
+'/' Operator
+'[' Punctuation
+'cls' Literal.String
+']' Punctuation
+')' Punctuation
+'*' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/peg/test_modified_strings.txt b/tests/snippets/peg/test_modified_strings.txt
new file mode 100644
index 0000000..f411226
--- /dev/null
+++ b/tests/snippets/peg/test_modified_strings.txt
@@ -0,0 +1,21 @@
+# see for example:
+# - http://textx.github.io/Arpeggio/
+# - https://nim-lang.org/docs/pegs.html
+# - https://github.com/erikrose/parsimonious
+# can't handle parsimonious-style regex while ~ is a cut operator
+
+---input---
+~"regex" i"insensitive" "multimod"ilx ("not modified")
+
+---tokens---
+'~' Operator
+'"regex"' Literal.String.Double
+' ' Text
+'i"insensitive"' Literal.String.Double
+' ' Text
+'"multimod"ilx' Literal.String.Double
+' ' Text
+'(' Punctuation
+'"not modified"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/peg/test_operators.txt b/tests/snippets/peg/test_operators.txt
new file mode 100644
index 0000000..35aee32
--- /dev/null
+++ b/tests/snippets/peg/test_operators.txt
@@ -0,0 +1,29 @@
+# see for example:
+# - https://github.com/gvanrossum/pegen
+# - https://nim-lang.org/docs/pegs.html
+
+---input---
+rule = 'a' | 'b'
+rule: 'a' ~ 'b'
+
+---tokens---
+'rule' Name.Class
+' ' Text
+'=' Operator
+' ' Text
+"'a'" Literal.String.Single
+' ' Text
+'|' Operator
+' ' Text
+"'b'" Literal.String.Single
+'\n' Text.Whitespace
+
+'rule' Name.Class
+':' Operator
+' ' Text
+"'a'" Literal.String.Single
+' ' Text
+'~' Operator
+' ' Text
+"'b'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/php/test_backslashes_in_strings.txt b/tests/snippets/php/test_backslashes_in_strings.txt
new file mode 100644
index 0000000..4bdbeb5
--- /dev/null
+++ b/tests/snippets/php/test_backslashes_in_strings.txt
@@ -0,0 +1,28 @@
+---input---
+<?php
+$pipe = popen( "flamegraph.pl --title=\"$title\" > /var/www/html/w/docs/flamegraph.svg", 'w' );
+
+---tokens---
+'<?php' Comment.Preproc
+'\n' Text
+
+'$pipe' Name.Variable
+' ' Text
+'=' Operator
+' ' Text
+'popen' Name.Builtin
+'(' Punctuation
+' ' Text
+'"' Literal.String.Double
+'flamegraph.pl --title=' Literal.String.Double
+'\\"' Literal.String.Escape
+'$title' Literal.String.Interpol
+'\\"' Literal.String.Escape
+' > /var/www/html/w/docs/flamegraph.svg' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+' ' Text
+"'w'" Literal.String.Single
+' ' Text
+');' Punctuation
+'\n' Text
diff --git a/tests/snippets/php/test_string_escaping_run.txt b/tests/snippets/php/test_string_escaping_run.txt
new file mode 100644
index 0000000..e31fbfc
--- /dev/null
+++ b/tests/snippets/php/test_string_escaping_run.txt
@@ -0,0 +1,16 @@
+---input---
+<?php $x="{\""; ?>
+
+---tokens---
+'<?php' Comment.Preproc
+' ' Text
+'$x' Name.Variable
+'=' Operator
+'"' Literal.String.Double
+'{' Literal.String.Double
+'\\"' Literal.String.Escape
+'"' Literal.String.Double
+';' Punctuation
+' ' Text
+'?>' Comment.Preproc
+'\n' Other
diff --git a/tests/snippets/powershell/test_colon_punctuation.txt b/tests/snippets/powershell/test_colon_punctuation.txt
new file mode 100644
index 0000000..72733c3
--- /dev/null
+++ b/tests/snippets/powershell/test_colon_punctuation.txt
@@ -0,0 +1,35 @@
+---input---
+$message = (Test-Path C:\) ? "Path exists" : "Path not found"
+[math]::pi
+
+---tokens---
+'$message' Name.Variable
+' ' Text
+'=' Punctuation
+' ' Text
+'(' Punctuation
+'Test-Path' Name.Builtin
+' ' Text
+'C' Name
+':' Punctuation
+'\\' Punctuation
+')' Punctuation
+' ' Text
+'?' Punctuation
+' ' Text
+'"' Literal.String.Double
+'Path exists' Literal.String.Double
+'"' Literal.String.Double
+' ' Text
+':' Punctuation
+' ' Text
+'"' Literal.String.Double
+'Path not found' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text
+
+'[math]' Name.Constant
+':' Punctuation
+':' Punctuation
+'pi' Name
+'\n' Text
diff --git a/tests/snippets/powershell/test_remoting_session.txt b/tests/snippets/powershell/test_remoting_session.txt
new file mode 100644
index 0000000..23fa9c2
--- /dev/null
+++ b/tests/snippets/powershell/test_remoting_session.txt
@@ -0,0 +1,19 @@
+---input---
+[Long-NetBIOS-Hostname]: PS C:\> Get-ChildItem
+
+---tokens---
+'[' Punctuation
+'Long' Name
+'-NetBIOS' Name
+'-Hostname' Name
+']' Punctuation
+':' Punctuation
+' ' Text
+'PS ' Name.Builtin
+'C' Name
+':' Punctuation
+'\\' Punctuation
+'>' Punctuation
+' ' Text
+'Get-ChildItem' Name.Builtin
+'\n' Text
diff --git a/tests/snippets/powershell/test_session.txt b/tests/snippets/powershell/test_session.txt
new file mode 100644
index 0000000..7fe8b42
--- /dev/null
+++ b/tests/snippets/powershell/test_session.txt
@@ -0,0 +1,28 @@
+---input---
+PS C:\> Get-ChildItem
+
+PS> Get-ChildItem
+
+PS > Get-ChildItem
+
+---tokens---
+'PS ' Name.Builtin
+'C' Name
+':' Punctuation
+'\\' Punctuation
+'>' Punctuation
+' ' Text
+'Get-ChildItem' Name.Builtin
+'\n\n' Text
+
+'PS' Name
+'>' Punctuation
+' ' Text
+'Get-ChildItem' Name.Builtin
+'\n\n' Text
+
+'PS ' Name.Builtin
+'>' Punctuation
+' ' Text
+'Get-ChildItem' Name.Builtin
+'\n' Text
diff --git a/tests/snippets/praat/test_broken_unquoted_string.txt b/tests/snippets/praat/test_broken_unquoted_string.txt
new file mode 100644
index 0000000..e108bb8
--- /dev/null
+++ b/tests/snippets/praat/test_broken_unquoted_string.txt
@@ -0,0 +1,16 @@
+---input---
+printline string
+... 'interpolated' string
+
+---tokens---
+'printline' Keyword
+' ' Text.Whitespace
+'string' Literal.String
+'\n' Text.Whitespace
+
+'...' Punctuation
+' ' Text.Whitespace
+"'interpolated'" Literal.String.Interpol
+' ' Text.Whitespace
+'string' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_function_call.txt b/tests/snippets/praat/test_function_call.txt
new file mode 100644
index 0000000..608ef16
--- /dev/null
+++ b/tests/snippets/praat/test_function_call.txt
@@ -0,0 +1,20 @@
+---input---
+selected("Sound", i+(a*b))
+
+---tokens---
+'selected' Name.Function
+'(' Punctuation
+'"' Literal.String
+'Sound' Literal.String
+'"' Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'i' Text
+'+' Operator
+'(' Text
+'a' Text
+'*' Operator
+'b' Text
+')' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_inline_if.txt b/tests/snippets/praat/test_inline_if.txt
new file mode 100644
index 0000000..e370263
--- /dev/null
+++ b/tests/snippets/praat/test_inline_if.txt
@@ -0,0 +1,27 @@
+---input---
+var = if true == 1 then -1 else 0 fi
+
+---tokens---
+'var' Text
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'if' Keyword
+' ' Text.Whitespace
+'true' Text
+' ' Text.Whitespace
+'==' Operator
+' ' Text.Whitespace
+'1' Literal.Number
+' ' Text.Whitespace
+'then' Keyword
+' ' Text.Whitespace
+'-' Operator
+'1' Literal.Number
+' ' Text.Whitespace
+'else' Keyword
+' ' Text.Whitespace
+'0' Literal.Number
+' ' Text.Whitespace
+'fi' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_indexed_numeric_with_precision.txt b/tests/snippets/praat/test_interpolated_indexed_numeric_with_precision.txt
new file mode 100644
index 0000000..0dc23e6
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_indexed_numeric_with_precision.txt
@@ -0,0 +1,6 @@
+---input---
+'a[3]:3'
+
+---tokens---
+"'a[3]:3'" Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_local_numeric_with_precision.txt b/tests/snippets/praat/test_interpolated_local_numeric_with_precision.txt
new file mode 100644
index 0000000..515fa9e
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_local_numeric_with_precision.txt
@@ -0,0 +1,6 @@
+---input---
+'a.a:3'
+
+---tokens---
+"'a.a:3'" Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_numeric_hash.txt b/tests/snippets/praat/test_interpolated_numeric_hash.txt
new file mode 100644
index 0000000..3d6d0f8
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_numeric_hash.txt
@@ -0,0 +1,6 @@
+---input---
+'a["b"]'
+
+---tokens---
+'\'a["b"]\'' Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_numeric_indexed.txt b/tests/snippets/praat/test_interpolated_numeric_indexed.txt
new file mode 100644
index 0000000..ea436f1
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_numeric_indexed.txt
@@ -0,0 +1,6 @@
+---input---
+'a[3]'
+
+---tokens---
+"'a[3]'" Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_numeric_with_precision.txt b/tests/snippets/praat/test_interpolated_numeric_with_precision.txt
new file mode 100644
index 0000000..8ab410d
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_numeric_with_precision.txt
@@ -0,0 +1,6 @@
+---input---
+'a:3'
+
+---tokens---
+"'a:3'" Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_string_hash.txt b/tests/snippets/praat/test_interpolated_string_hash.txt
new file mode 100644
index 0000000..49f5147
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_string_hash.txt
@@ -0,0 +1,6 @@
+---input---
+'a$["b"]'
+
+---tokens---
+'\'a$["b"]\'' Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolated_string_indexed.txt b/tests/snippets/praat/test_interpolated_string_indexed.txt
new file mode 100644
index 0000000..7a845d7
--- /dev/null
+++ b/tests/snippets/praat/test_interpolated_string_indexed.txt
@@ -0,0 +1,6 @@
+---input---
+'a$[3]'
+
+---tokens---
+"'a$[3]'" Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_interpolation_boundary.txt b/tests/snippets/praat/test_interpolation_boundary.txt
new file mode 100644
index 0000000..1f3b14b
--- /dev/null
+++ b/tests/snippets/praat/test_interpolation_boundary.txt
@@ -0,0 +1,14 @@
+---input---
+"'" + "'"
+
+---tokens---
+'"' Literal.String
+"'" Literal.String
+'"' Literal.String
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'"' Literal.String
+"'" Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_numeric_assignment.txt b/tests/snippets/praat/test_numeric_assignment.txt
new file mode 100644
index 0000000..55fc1e6
--- /dev/null
+++ b/tests/snippets/praat/test_numeric_assignment.txt
@@ -0,0 +1,11 @@
+---input---
+var = -15e4
+
+---tokens---
+'var' Text
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'-' Operator
+'15e4' Literal.Number
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_string_assignment.txt b/tests/snippets/praat/test_string_assignment.txt
new file mode 100644
index 0000000..baff23d
--- /dev/null
+++ b/tests/snippets/praat/test_string_assignment.txt
@@ -0,0 +1,12 @@
+---input---
+var$ = "foo"
+
+---tokens---
+'var$' Text
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"' Literal.String
+'foo' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/praat/test_string_escaped_quotes.txt b/tests/snippets/praat/test_string_escaped_quotes.txt
new file mode 100644
index 0000000..1cf6330
--- /dev/null
+++ b/tests/snippets/praat/test_string_escaped_quotes.txt
@@ -0,0 +1,13 @@
+---input---
+"it said ""foo"""
+
+---tokens---
+'"' Literal.String
+'it said ' Literal.String
+'"' Literal.String
+'"' Literal.String
+'foo' Literal.String
+'"' Literal.String
+'"' Literal.String
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_complex_exp_single_quotes.txt b/tests/snippets/promql/test_complex_exp_single_quotes.txt
new file mode 100644
index 0000000..cbbde3a
--- /dev/null
+++ b/tests/snippets/promql/test_complex_exp_single_quotes.txt
@@ -0,0 +1,35 @@
+---input---
+(sum(rate(metric_test_app{app='turtle',proc='web'}[2m])) by(node))
+
+---tokens---
+'(' Operator
+'sum' Keyword
+'(' Operator
+'rate' Keyword.Reserved
+'(' Operator
+'metric_test_app' Name.Variable
+'{' Punctuation
+'app' Name.Label
+'=' Operator
+"'" Punctuation
+'turtle' Literal.String
+"'" Punctuation
+',' Punctuation
+'proc' Name.Label
+'=' Operator
+"'" Punctuation
+'web' Literal.String
+"'" Punctuation
+'}' Punctuation
+'[' Punctuation
+'2m' Literal.String
+']' Punctuation
+')' Operator
+')' Operator
+' ' Text.Whitespace
+'by' Keyword
+'(' Operator
+'node' Name.Variable
+')' Operator
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_expression_and_comment.txt b/tests/snippets/promql/test_expression_and_comment.txt
new file mode 100644
index 0000000..3ee2bfa
--- /dev/null
+++ b/tests/snippets/promql/test_expression_and_comment.txt
@@ -0,0 +1,15 @@
+---input---
+go_gc_duration_seconds{instance="localhost:9090"} # single comment
+
+---tokens---
+'go_gc_duration_seconds' Name.Variable
+'{' Punctuation
+'instance' Name.Label
+'=' Operator
+'"' Punctuation
+'localhost:9090' Literal.String
+'"' Punctuation
+'}' Punctuation
+' ' Text.Whitespace
+'# single comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_function_delta.txt b/tests/snippets/promql/test_function_delta.txt
new file mode 100644
index 0000000..8b1f9b0
--- /dev/null
+++ b/tests/snippets/promql/test_function_delta.txt
@@ -0,0 +1,19 @@
+---input---
+delta(cpu_temp_celsius{host="zeus"}[2h])
+
+---tokens---
+'delta' Keyword.Reserved
+'(' Operator
+'cpu_temp_celsius' Name.Variable
+'{' Punctuation
+'host' Name.Label
+'=' Operator
+'"' Punctuation
+'zeus' Literal.String
+'"' Punctuation
+'}' Punctuation
+'[' Punctuation
+'2h' Literal.String
+']' Punctuation
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_function_multi_line.txt b/tests/snippets/promql/test_function_multi_line.txt
new file mode 100644
index 0000000..31664cc
--- /dev/null
+++ b/tests/snippets/promql/test_function_multi_line.txt
@@ -0,0 +1,80 @@
+---input---
+label_replace(
+ sum by (instance) (
+ irate(node_disk_read_bytes_total[2m])
+ ) / 1024 / 1024,
+ "device",
+ 'disk',
+ "instance",
+ ".*"
+)
+
+---tokens---
+'label_replace' Keyword.Reserved
+'(' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'sum' Keyword
+' ' Text.Whitespace
+'by' Keyword
+' ' Text.Whitespace
+'(' Operator
+'instance' Name.Variable
+')' Operator
+' ' Text.Whitespace
+'(' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'irate' Keyword.Reserved
+'(' Operator
+'node_disk_read_bytes_total' Name.Variable
+'[' Punctuation
+'2m' Literal.String
+']' Punctuation
+')' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+')' Operator
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'1024' Literal.Number.Integer
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'1024' Literal.Number.Integer
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'device' Literal.String
+'"' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+"'" Punctuation
+'disk' Literal.String
+"'" Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'instance' Literal.String
+'"' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'.*' Literal.String
+'"' Punctuation
+'\n' Text.Whitespace
+
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_function_multi_line_with_offset.txt b/tests/snippets/promql/test_function_multi_line_with_offset.txt
new file mode 100644
index 0000000..a7462fb
--- /dev/null
+++ b/tests/snippets/promql/test_function_multi_line_with_offset.txt
@@ -0,0 +1,87 @@
+---input---
+label_replace(
+ avg by(instance)
+ (irate(node_cpu_seconds_total{mode = "idle"}[5m] offset 3s)
+ ) * 100,
+ "device",
+ "cpu",
+ "instance",
+ ".*"
+)
+
+---tokens---
+'label_replace' Keyword.Reserved
+'(' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'avg' Keyword
+' ' Text.Whitespace
+'by' Keyword
+'(' Operator
+'instance' Name.Variable
+')' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'(' Operator
+'irate' Keyword.Reserved
+'(' Operator
+'node_cpu_seconds_total' Name.Variable
+'{' Punctuation
+'mode' Name.Label
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"' Punctuation
+'idle' Literal.String
+'"' Punctuation
+'}' Punctuation
+'[' Punctuation
+'5m' Literal.String
+']' Punctuation
+' ' Text.Whitespace
+'offset' Keyword
+' ' Text.Whitespace
+'3s' Literal.String
+')' Operator
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+')' Operator
+' ' Text.Whitespace
+'*' Operator
+' ' Text.Whitespace
+'100' Literal.Number.Integer
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'device' Literal.String
+'"' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'cpu' Literal.String
+'"' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'instance' Literal.String
+'"' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"' Punctuation
+'.*' Literal.String
+'"' Punctuation
+'\n' Text.Whitespace
+
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_function_sum_with_args.txt b/tests/snippets/promql/test_function_sum_with_args.txt
new file mode 100644
index 0000000..3d677e1
--- /dev/null
+++ b/tests/snippets/promql/test_function_sum_with_args.txt
@@ -0,0 +1,19 @@
+---input---
+sum by (app, proc) (instance_memory_usage_bytes)
+
+---tokens---
+'sum' Keyword
+' ' Text.Whitespace
+'by' Keyword
+' ' Text.Whitespace
+'(' Operator
+'app' Name.Variable
+',' Punctuation
+' ' Text.Whitespace
+'proc' Name.Variable
+')' Operator
+' ' Text.Whitespace
+'(' Operator
+'instance_memory_usage_bytes' Name.Variable
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_matching_operator_no_regex_match.txt b/tests/snippets/promql/test_matching_operator_no_regex_match.txt
new file mode 100644
index 0000000..9859739
--- /dev/null
+++ b/tests/snippets/promql/test_matching_operator_no_regex_match.txt
@@ -0,0 +1,16 @@
+---input---
+metric_test_app{status!~'(4|5)..'}[2m]
+
+---tokens---
+'metric_test_app' Name.Variable
+'{' Punctuation
+'status' Name.Label
+'!~' Operator
+"'" Punctuation
+'(4|5)..' Literal.String
+"'" Punctuation
+'}' Punctuation
+'[' Punctuation
+'2m' Literal.String
+']' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_metric.txt b/tests/snippets/promql/test_metric.txt
new file mode 100644
index 0000000..e4889cf
--- /dev/null
+++ b/tests/snippets/promql/test_metric.txt
@@ -0,0 +1,6 @@
+---input---
+go_gc_duration_seconds
+
+---tokens---
+'go_gc_duration_seconds' Name.Variable
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_metric_multiple_labels.txt b/tests/snippets/promql/test_metric_multiple_labels.txt
new file mode 100644
index 0000000..bcb0b64
--- /dev/null
+++ b/tests/snippets/promql/test_metric_multiple_labels.txt
@@ -0,0 +1,19 @@
+---input---
+go_gc_duration_seconds{instance="localhost:9090",job="alertmanager"}
+
+---tokens---
+'go_gc_duration_seconds' Name.Variable
+'{' Punctuation
+'instance' Name.Label
+'=' Operator
+'"' Punctuation
+'localhost:9090' Literal.String
+'"' Punctuation
+',' Punctuation
+'job' Name.Label
+'=' Operator
+'"' Punctuation
+'alertmanager' Literal.String
+'"' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_metric_multiple_labels_with_spaces.txt b/tests/snippets/promql/test_metric_multiple_labels_with_spaces.txt
new file mode 100644
index 0000000..3ca3d49
--- /dev/null
+++ b/tests/snippets/promql/test_metric_multiple_labels_with_spaces.txt
@@ -0,0 +1,22 @@
+---input---
+go_gc_duration_seconds{ instance="localhost:9090", job="alertmanager" }
+
+---tokens---
+'go_gc_duration_seconds' Name.Variable
+'{' Punctuation
+' ' Text.Whitespace
+'instance' Name.Label
+'=' Operator
+'"' Punctuation
+'localhost:9090' Literal.String
+'"' Punctuation
+',' Punctuation
+' ' Text.Whitespace
+'job' Name.Label
+'=' Operator
+'"' Punctuation
+'alertmanager' Literal.String
+'"' Punctuation
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/promql/test_metric_one_label.txt b/tests/snippets/promql/test_metric_one_label.txt
new file mode 100644
index 0000000..8baeafb
--- /dev/null
+++ b/tests/snippets/promql/test_metric_one_label.txt
@@ -0,0 +1,13 @@
+---input---
+go_gc_duration_seconds{instance="localhost:9090"}
+
+---tokens---
+'go_gc_duration_seconds' Name.Variable
+'{' Punctuation
+'instance' Name.Label
+'=' Operator
+'"' Punctuation
+'localhost:9090' Literal.String
+'"' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_comments.txt b/tests/snippets/properties/test_comments.txt
new file mode 100644
index 0000000..9bc6586
--- /dev/null
+++ b/tests/snippets/properties/test_comments.txt
@@ -0,0 +1,12 @@
+# Assures lines lead by either # or ! are recognized as a comment
+
+---input---
+! a comment
+# also a comment
+
+---tokens---
+'! a comment' Comment.Single
+'\n' Text.Whitespace
+
+'# also a comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_escaped_space_in_key.txt b/tests/snippets/properties/test_escaped_space_in_key.txt
new file mode 100644
index 0000000..eef0292
--- /dev/null
+++ b/tests/snippets/properties/test_escaped_space_in_key.txt
@@ -0,0 +1,10 @@
+---input---
+key = value
+
+---tokens---
+'key' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'value' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_escaped_space_in_value.txt b/tests/snippets/properties/test_escaped_space_in_value.txt
new file mode 100644
index 0000000..f76507f
--- /dev/null
+++ b/tests/snippets/properties/test_escaped_space_in_value.txt
@@ -0,0 +1,10 @@
+---input---
+key = doubleword\ value
+
+---tokens---
+'key' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'doubleword\\ value' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_just_key.txt b/tests/snippets/properties/test_just_key.txt
new file mode 100644
index 0000000..2665239
--- /dev/null
+++ b/tests/snippets/properties/test_just_key.txt
@@ -0,0 +1,6 @@
+---input---
+justkey
+
+---tokens---
+'justkey' Name.Attribute
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_just_key_with_space.txt b/tests/snippets/properties/test_just_key_with_space.txt
new file mode 100644
index 0000000..660c37c
--- /dev/null
+++ b/tests/snippets/properties/test_just_key_with_space.txt
@@ -0,0 +1,6 @@
+---input---
+just\ key
+
+---tokens---
+'just\\ key' Name.Attribute
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_leading_whitespace_comments.txt b/tests/snippets/properties/test_leading_whitespace_comments.txt
new file mode 100644
index 0000000..3a36afc
--- /dev/null
+++ b/tests/snippets/properties/test_leading_whitespace_comments.txt
@@ -0,0 +1,6 @@
+---input---
+# comment
+
+---tokens---
+'# comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/properties/test_space_delimited_kv_pair.txt b/tests/snippets/properties/test_space_delimited_kv_pair.txt
new file mode 100644
index 0000000..98961e4
--- /dev/null
+++ b/tests/snippets/properties/test_space_delimited_kv_pair.txt
@@ -0,0 +1,8 @@
+---input---
+key value
+
+---tokens---
+'key' Name.Attribute
+' ' Text.Whitespace
+'value' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/pwsh-session/test_continuation.txt b/tests/snippets/pwsh-session/test_continuation.txt
new file mode 100644
index 0000000..4735d36
--- /dev/null
+++ b/tests/snippets/pwsh-session/test_continuation.txt
@@ -0,0 +1,124 @@
+---input---
+PS> python -m doctest `
+> -o DONT_ACCEPT_TRUE_FOR_1 `
+> -o ELLIPSIS options.txt
+
+PS> $Params = @{
+> Height = 50
+> Width = 50
+> Depth = 50
+> Name = 'My Widget'
+> ID = '10dbe43f-0269-48b8-96cb-447a755add55'
+> }
+
+
+PS> ls |
+> grep "python"
+
+---tokens---
+'PS> ' Generic.Prompt
+'python' Name
+' ' Text
+'-m' Name
+' ' Text
+'doctest' Name
+' ' Text
+'`' Punctuation
+'\n' Text
+
+'> ' Generic.Prompt
+'-o' Name
+' ' Text
+'DONT_ACCEPT_TRUE_FOR_1' Name
+' ' Text
+'`' Punctuation
+'\n' Text
+
+'> ' Generic.Prompt
+'-o' Name
+' ' Text
+'ELLIPSIS' Name
+' ' Text
+'options' Name
+'.' Punctuation
+'txt' Name
+'\n' Text
+
+'\n' Generic.Output
+
+'PS> ' Generic.Prompt
+' ' Text
+'$Params' Name.Variable
+' ' Text
+'=' Punctuation
+' ' Text
+'@' Punctuation
+'{' Punctuation
+'\n' Text
+
+'> ' Generic.Prompt
+' ' Text
+'Height' Name
+' ' Text
+'=' Punctuation
+' ' Text
+'50' Name
+'\n' Text
+
+'> ' Generic.Prompt
+' ' Text
+'Width' Name
+' ' Text
+'=' Punctuation
+' ' Text
+'50' Name
+'\n' Text
+
+'> ' Generic.Prompt
+' ' Text
+'Depth' Name
+' ' Text
+'=' Punctuation
+' ' Text
+'50' Name
+'\n' Text
+
+'> ' Generic.Prompt
+' ' Text
+'Name' Name
+' ' Text
+'=' Punctuation
+' ' Text
+"'My Widget'" Literal.String.Single
+'\n' Text
+
+'> ' Generic.Prompt
+' ' Text
+'ID' Name
+' ' Text
+'=' Punctuation
+' ' Text
+"'10dbe43f-0269-48b8-96cb-447a755add55'" Literal.String.Single
+'\n' Text
+
+'> ' Generic.Prompt
+'}' Punctuation
+'\n' Text
+
+'\n' Generic.Output
+
+'\n' Generic.Output
+
+'PS> ' Generic.Prompt
+' ' Text
+'ls ' Name.Builtin
+'|' Punctuation
+'\n' Text
+
+'> ' Generic.Prompt
+'grep' Name
+' ' Text
+'"' Literal.String.Double
+'python' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text
diff --git a/tests/snippets/python/test_bytes_escape_codes.txt b/tests/snippets/python/test_bytes_escape_codes.txt
new file mode 100644
index 0000000..c1a3443
--- /dev/null
+++ b/tests/snippets/python/test_bytes_escape_codes.txt
@@ -0,0 +1,24 @@
+---input---
+b'\\ \n \x12 \777 \u1234 \U00010348 \N{Plus-Minus Sign}'
+
+---tokens---
+'b' Literal.String.Affix
+"'" Literal.String.Single
+'\\\\' Literal.String.Escape
+' ' Literal.String.Single
+'\\n' Literal.String.Escape
+' ' Literal.String.Single
+'\\x12' Literal.String.Escape
+' ' Literal.String.Single
+'\\777' Literal.String.Escape
+' ' Literal.String.Single
+'\\' Literal.String.Single
+'u1234 ' Literal.String.Single
+'\\' Literal.String.Single
+'U00010348 ' Literal.String.Single
+'\\' Literal.String.Single
+'N' Literal.String.Single
+'{' Literal.String.Single
+'Plus-Minus Sign}' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_floats.txt b/tests/snippets/python/test_floats.txt
new file mode 100644
index 0000000..3ae9dc9
--- /dev/null
+++ b/tests/snippets/python/test_floats.txt
@@ -0,0 +1,75 @@
+---input---
+123 -11 0 -0 0.5 .5 1. -0.5 +0.5 -.5 -1. 2e1 -2e1 2e -2e +2e e.3 -e.3 11.2e-3 -11.2e-3 5_6 5__6 _5 6_ 5.6_7 5.67_
+
+---tokens---
+'123' Literal.Number.Integer
+' ' Text
+'-' Operator
+'11' Literal.Number.Integer
+' ' Text
+'0' Literal.Number.Integer
+' ' Text
+'-' Operator
+'0' Literal.Number.Integer
+' ' Text
+'0.5' Literal.Number.Float
+' ' Text
+'.5' Literal.Number.Float
+' ' Text
+'1.' Literal.Number.Float
+' ' Text
+'-' Operator
+'0.5' Literal.Number.Float
+' ' Text
+'+' Operator
+'0.5' Literal.Number.Float
+' ' Text
+'-' Operator
+'.5' Literal.Number.Float
+' ' Text
+'-' Operator
+'1.' Literal.Number.Float
+' ' Text
+'2e1' Literal.Number.Float
+' ' Text
+'-' Operator
+'2e1' Literal.Number.Float
+' ' Text
+'2' Literal.Number.Integer
+'e' Name
+' ' Text
+'-' Operator
+'2' Literal.Number.Integer
+'e' Name
+' ' Text
+'+' Operator
+'2' Literal.Number.Integer
+'e' Name
+' ' Text
+'e' Name
+'.3' Literal.Number.Float
+' ' Text
+'-' Operator
+'e' Name
+'.3' Literal.Number.Float
+' ' Text
+'11.2e-3' Literal.Number.Float
+' ' Text
+'-' Operator
+'11.2e-3' Literal.Number.Float
+' ' Text
+'5_6' Literal.Number.Integer
+' ' Text
+'5' Literal.Number.Integer
+'__6' Name
+' ' Text
+'_5' Name
+' ' Text
+'6' Literal.Number.Integer
+'_' Name
+' ' Text
+'5.6_7' Literal.Number.Float
+' ' Text
+'5.67' Literal.Number.Float
+'_' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_01a.txt b/tests/snippets/python/test_fstring_01a.txt
new file mode 100644
index 0000000..a3b29a3
--- /dev/null
+++ b/tests/snippets/python/test_fstring_01a.txt
@@ -0,0 +1,25 @@
+---input---
+f'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}.'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'My name is ' Literal.String.Single
+'{' Literal.String.Interpol
+'name' Name
+'}' Literal.String.Interpol
+', my age next year is ' Literal.String.Single
+'{' Literal.String.Interpol
+'age' Name
+'+' Operator
+'1' Literal.Number.Integer
+'}' Literal.String.Interpol
+', my anniversary is ' Literal.String.Single
+'{' Literal.String.Interpol
+'anniversary' Name
+':' Literal.String.Interpol
+'%A, %B %d, %Y' Literal.String.Single
+'}' Literal.String.Interpol
+'.' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_01b.txt b/tests/snippets/python/test_fstring_01b.txt
new file mode 100644
index 0000000..0d2c343
--- /dev/null
+++ b/tests/snippets/python/test_fstring_01b.txt
@@ -0,0 +1,25 @@
+---input---
+f"My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}."
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'My name is ' Literal.String.Double
+'{' Literal.String.Interpol
+'name' Name
+'}' Literal.String.Interpol
+', my age next year is ' Literal.String.Double
+'{' Literal.String.Interpol
+'age' Name
+'+' Operator
+'1' Literal.Number.Integer
+'}' Literal.String.Interpol
+', my anniversary is ' Literal.String.Double
+'{' Literal.String.Interpol
+'anniversary' Name
+':' Literal.String.Interpol
+'%A, %B %d, %Y' Literal.String.Double
+'}' Literal.String.Interpol
+'.' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_02a.txt b/tests/snippets/python/test_fstring_02a.txt
new file mode 100644
index 0000000..cfd41a9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_02a.txt
@@ -0,0 +1,13 @@
+---input---
+f'He said his name is {name!r}.'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'He said his name is ' Literal.String.Single
+'{' Literal.String.Interpol
+'name' Name
+'!r}' Literal.String.Interpol
+'.' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_02b.txt b/tests/snippets/python/test_fstring_02b.txt
new file mode 100644
index 0000000..c72a77b
--- /dev/null
+++ b/tests/snippets/python/test_fstring_02b.txt
@@ -0,0 +1,13 @@
+---input---
+f"He said his name is {name!r}."
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'He said his name is ' Literal.String.Double
+'{' Literal.String.Interpol
+'name' Name
+'!r}' Literal.String.Interpol
+'.' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_03a.txt b/tests/snippets/python/test_fstring_03a.txt
new file mode 100644
index 0000000..366e495
--- /dev/null
+++ b/tests/snippets/python/test_fstring_03a.txt
@@ -0,0 +1,14 @@
+---input---
+f'input={value:#06x}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'input=' Literal.String.Single
+'{' Literal.String.Interpol
+'value' Name
+':' Literal.String.Interpol
+'#06x' Literal.String.Single
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_03b.txt b/tests/snippets/python/test_fstring_03b.txt
new file mode 100644
index 0000000..260e56d
--- /dev/null
+++ b/tests/snippets/python/test_fstring_03b.txt
@@ -0,0 +1,14 @@
+---input---
+f"input={value:#06x}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'input=' Literal.String.Double
+'{' Literal.String.Interpol
+'value' Name
+':' Literal.String.Interpol
+'#06x' Literal.String.Double
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_04a.txt b/tests/snippets/python/test_fstring_04a.txt
new file mode 100644
index 0000000..58516c9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_04a.txt
@@ -0,0 +1,13 @@
+---input---
+f'{"quoted string"}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'"' Literal.String.Double
+'quoted string' Literal.String.Double
+'"' Literal.String.Double
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_04b.txt b/tests/snippets/python/test_fstring_04b.txt
new file mode 100644
index 0000000..40e1ea0
--- /dev/null
+++ b/tests/snippets/python/test_fstring_04b.txt
@@ -0,0 +1,13 @@
+---input---
+f"{'quoted string'}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+"'" Literal.String.Single
+'quoted string' Literal.String.Single
+"'" Literal.String.Single
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_05a.txt b/tests/snippets/python/test_fstring_05a.txt
new file mode 100644
index 0000000..77a5c10
--- /dev/null
+++ b/tests/snippets/python/test_fstring_05a.txt
@@ -0,0 +1,16 @@
+---input---
+f'{f"{inner}"}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'inner' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_05b.txt b/tests/snippets/python/test_fstring_05b.txt
new file mode 100644
index 0000000..9a0dc5a
--- /dev/null
+++ b/tests/snippets/python/test_fstring_05b.txt
@@ -0,0 +1,16 @@
+---input---
+f"{f'{inner}'}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'inner' Name
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_06a.txt b/tests/snippets/python/test_fstring_06a.txt
new file mode 100644
index 0000000..b8b33c0
--- /dev/null
+++ b/tests/snippets/python/test_fstring_06a.txt
@@ -0,0 +1,16 @@
+# SyntaxError: f-string expression part cannot include a backslash
+
+---input---
+f'{\'quoted string\'}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'\\' Error
+"'" Literal.String.Single
+'quoted string' Literal.String.Single
+"\\'" Literal.String.Escape
+'}' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_06b.txt b/tests/snippets/python/test_fstring_06b.txt
new file mode 100644
index 0000000..0712321
--- /dev/null
+++ b/tests/snippets/python/test_fstring_06b.txt
@@ -0,0 +1,16 @@
+# SyntaxError: f-string expression part cannot include a backslash
+
+---input---
+f"{\"quoted string\"}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'\\' Error
+'"' Literal.String.Double
+'quoted string' Literal.String.Double
+'\\"' Literal.String.Escape
+'}' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_07a.txt b/tests/snippets/python/test_fstring_07a.txt
new file mode 100644
index 0000000..c597ea9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_07a.txt
@@ -0,0 +1,17 @@
+---input---
+f'{{ {4*10} }}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{{' Literal.String.Escape
+' ' Literal.String.Single
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+' ' Literal.String.Single
+'}}' Literal.String.Escape
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_07b.txt b/tests/snippets/python/test_fstring_07b.txt
new file mode 100644
index 0000000..413c158
--- /dev/null
+++ b/tests/snippets/python/test_fstring_07b.txt
@@ -0,0 +1,17 @@
+---input---
+f"{{ {4*10} }}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{{' Literal.String.Escape
+' ' Literal.String.Double
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+' ' Literal.String.Double
+'}}' Literal.String.Escape
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_08a.txt b/tests/snippets/python/test_fstring_08a.txt
new file mode 100644
index 0000000..39dd887
--- /dev/null
+++ b/tests/snippets/python/test_fstring_08a.txt
@@ -0,0 +1,15 @@
+---input---
+f'{{{4*10}}}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{{' Literal.String.Escape
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+'}}' Literal.String.Escape
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_08b.txt b/tests/snippets/python/test_fstring_08b.txt
new file mode 100644
index 0000000..458c697
--- /dev/null
+++ b/tests/snippets/python/test_fstring_08b.txt
@@ -0,0 +1,15 @@
+---input---
+f"{{{4*10}}}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{{' Literal.String.Escape
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+'}}' Literal.String.Escape
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_09a.txt b/tests/snippets/python/test_fstring_09a.txt
new file mode 100644
index 0000000..00f3e7f
--- /dev/null
+++ b/tests/snippets/python/test_fstring_09a.txt
@@ -0,0 +1,14 @@
+---input---
+fr'x={4*10}'
+
+---tokens---
+'fr' Literal.String.Affix
+"'" Literal.String.Single
+'x=' Literal.String.Single
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_09b.txt b/tests/snippets/python/test_fstring_09b.txt
new file mode 100644
index 0000000..01d74e9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_09b.txt
@@ -0,0 +1,14 @@
+---input---
+fr"x={4*10}"
+
+---tokens---
+'fr' Literal.String.Affix
+'"' Literal.String.Double
+'x=' Literal.String.Double
+'{' Literal.String.Interpol
+'4' Literal.Number.Integer
+'*' Operator
+'10' Literal.Number.Integer
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_10a.txt b/tests/snippets/python/test_fstring_10a.txt
new file mode 100644
index 0000000..a2e11ba
--- /dev/null
+++ b/tests/snippets/python/test_fstring_10a.txt
@@ -0,0 +1,18 @@
+---input---
+f'abc {a["x"]} def'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'abc ' Literal.String.Single
+'{' Literal.String.Interpol
+'a' Name
+'[' Punctuation
+'"' Literal.String.Double
+'x' Literal.String.Double
+'"' Literal.String.Double
+']' Punctuation
+'}' Literal.String.Interpol
+' def' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_10b.txt b/tests/snippets/python/test_fstring_10b.txt
new file mode 100644
index 0000000..21d116c
--- /dev/null
+++ b/tests/snippets/python/test_fstring_10b.txt
@@ -0,0 +1,18 @@
+---input---
+f"abc {a['x']} def"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'abc ' Literal.String.Double
+'{' Literal.String.Interpol
+'a' Name
+'[' Punctuation
+"'" Literal.String.Single
+'x' Literal.String.Single
+"'" Literal.String.Single
+']' Punctuation
+'}' Literal.String.Interpol
+' def' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_11a.txt b/tests/snippets/python/test_fstring_11a.txt
new file mode 100644
index 0000000..ce20e7b
--- /dev/null
+++ b/tests/snippets/python/test_fstring_11a.txt
@@ -0,0 +1,18 @@
+---input---
+f'''abc {a['x']} def'''
+
+---tokens---
+'f' Literal.String.Affix
+"'''" Literal.String.Single
+'abc ' Literal.String.Single
+'{' Literal.String.Interpol
+'a' Name
+'[' Punctuation
+"'" Literal.String.Single
+'x' Literal.String.Single
+"'" Literal.String.Single
+']' Punctuation
+'}' Literal.String.Interpol
+' def' Literal.String.Single
+"'''" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_11b.txt b/tests/snippets/python/test_fstring_11b.txt
new file mode 100644
index 0000000..baf1370
--- /dev/null
+++ b/tests/snippets/python/test_fstring_11b.txt
@@ -0,0 +1,18 @@
+---input---
+f"""abc {a["x"]} def"""
+
+---tokens---
+'f' Literal.String.Affix
+'"""' Literal.String.Double
+'abc ' Literal.String.Double
+'{' Literal.String.Interpol
+'a' Name
+'[' Punctuation
+'"' Literal.String.Double
+'x' Literal.String.Double
+'"' Literal.String.Double
+']' Punctuation
+'}' Literal.String.Interpol
+' def' Literal.String.Double
+'"""' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_12a.txt b/tests/snippets/python/test_fstring_12a.txt
new file mode 100644
index 0000000..9988804
--- /dev/null
+++ b/tests/snippets/python/test_fstring_12a.txt
@@ -0,0 +1,16 @@
+---input---
+f'''{x
++1}'''
+
+---tokens---
+'f' Literal.String.Affix
+"'''" Literal.String.Single
+'{' Literal.String.Interpol
+'x' Name
+'\n' Text.Whitespace
+
+'+' Operator
+'1' Literal.Number.Integer
+'}' Literal.String.Interpol
+"'''" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_12b.txt b/tests/snippets/python/test_fstring_12b.txt
new file mode 100644
index 0000000..878b7a0
--- /dev/null
+++ b/tests/snippets/python/test_fstring_12b.txt
@@ -0,0 +1,16 @@
+---input---
+f"""{x
++1}"""
+
+---tokens---
+'f' Literal.String.Affix
+'"""' Literal.String.Double
+'{' Literal.String.Interpol
+'x' Name
+'\n' Text.Whitespace
+
+'+' Operator
+'1' Literal.Number.Integer
+'}' Literal.String.Interpol
+'"""' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_13a.txt b/tests/snippets/python/test_fstring_13a.txt
new file mode 100644
index 0000000..e6e97e8
--- /dev/null
+++ b/tests/snippets/python/test_fstring_13a.txt
@@ -0,0 +1,17 @@
+---input---
+f'''{d[0
+]}'''
+
+---tokens---
+'f' Literal.String.Affix
+"'''" Literal.String.Single
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
+
+']' Punctuation
+'}' Literal.String.Interpol
+"'''" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_13b.txt b/tests/snippets/python/test_fstring_13b.txt
new file mode 100644
index 0000000..0a3aa56
--- /dev/null
+++ b/tests/snippets/python/test_fstring_13b.txt
@@ -0,0 +1,17 @@
+---input---
+f"""{d[0
+]}"""
+
+---tokens---
+'f' Literal.String.Affix
+'"""' Literal.String.Double
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
+
+']' Punctuation
+'}' Literal.String.Interpol
+'"""' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_14a.txt b/tests/snippets/python/test_fstring_14a.txt
new file mode 100644
index 0000000..a826835
--- /dev/null
+++ b/tests/snippets/python/test_fstring_14a.txt
@@ -0,0 +1,20 @@
+---input---
+f'result: {value:{width}.{precision}}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'result: ' Literal.String.Single
+'{' Literal.String.Interpol
+'value' Name
+':' Literal.String.Interpol
+'{' Literal.String.Interpol
+'width' Name
+'}' Literal.String.Interpol
+'.' Literal.String.Single
+'{' Literal.String.Interpol
+'precision' Name
+'}' Literal.String.Interpol
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_14b.txt b/tests/snippets/python/test_fstring_14b.txt
new file mode 100644
index 0000000..3100883
--- /dev/null
+++ b/tests/snippets/python/test_fstring_14b.txt
@@ -0,0 +1,20 @@
+---input---
+f"result: {value:{width}.{precision}}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'result: ' Literal.String.Double
+'{' Literal.String.Interpol
+'value' Name
+':' Literal.String.Interpol
+'{' Literal.String.Interpol
+'width' Name
+'}' Literal.String.Interpol
+'.' Literal.String.Double
+'{' Literal.String.Interpol
+'precision' Name
+'}' Literal.String.Interpol
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_15a.txt b/tests/snippets/python/test_fstring_15a.txt
new file mode 100644
index 0000000..f8f62b8
--- /dev/null
+++ b/tests/snippets/python/test_fstring_15a.txt
@@ -0,0 +1,42 @@
+---input---
+'a' 'b' f'{x}' '{c}' f'str<{y:^4}>' 'd' 'e'
+
+---tokens---
+"'" Literal.String.Single
+'a' Literal.String.Single
+"'" Literal.String.Single
+' ' Text
+"'" Literal.String.Single
+'b' Literal.String.Single
+"'" Literal.String.Single
+' ' Text
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'x' Name
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+' ' Text
+"'" Literal.String.Single
+'{c}' Literal.String.Interpol
+"'" Literal.String.Single
+' ' Text
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'str<' Literal.String.Single
+'{' Literal.String.Interpol
+'y' Name
+':' Literal.String.Interpol
+'^4' Literal.String.Single
+'}' Literal.String.Interpol
+'>' Literal.String.Single
+"'" Literal.String.Single
+' ' Text
+"'" Literal.String.Single
+'d' Literal.String.Single
+"'" Literal.String.Single
+' ' Text
+"'" Literal.String.Single
+'e' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_15b.txt b/tests/snippets/python/test_fstring_15b.txt
new file mode 100644
index 0000000..7b5307c
--- /dev/null
+++ b/tests/snippets/python/test_fstring_15b.txt
@@ -0,0 +1,42 @@
+---input---
+"a" "b" f"{x}" "{c}" f"str<{y:^4}>" "d" "e"
+
+---tokens---
+'"' Literal.String.Double
+'a' Literal.String.Double
+'"' Literal.String.Double
+' ' Text
+'"' Literal.String.Double
+'b' Literal.String.Double
+'"' Literal.String.Double
+' ' Text
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'x' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text
+'"' Literal.String.Double
+'{c}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'str<' Literal.String.Double
+'{' Literal.String.Interpol
+'y' Name
+':' Literal.String.Interpol
+'^4' Literal.String.Double
+'}' Literal.String.Interpol
+'>' Literal.String.Double
+'"' Literal.String.Double
+' ' Text
+'"' Literal.String.Double
+'d' Literal.String.Double
+'"' Literal.String.Double
+' ' Text
+'"' Literal.String.Double
+'e' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_16a.txt b/tests/snippets/python/test_fstring_16a.txt
new file mode 100644
index 0000000..15b11e8
--- /dev/null
+++ b/tests/snippets/python/test_fstring_16a.txt
@@ -0,0 +1,18 @@
+---input---
+f'{i}:{d[i]}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'i' Name
+'}' Literal.String.Interpol
+':' Literal.String.Single
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'i' Name
+']' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_16b.txt b/tests/snippets/python/test_fstring_16b.txt
new file mode 100644
index 0000000..e917516
--- /dev/null
+++ b/tests/snippets/python/test_fstring_16b.txt
@@ -0,0 +1,18 @@
+---input---
+f"{i}:{d[i]}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'i' Name
+'}' Literal.String.Interpol
+':' Literal.String.Double
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'i' Name
+']' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_17a.txt b/tests/snippets/python/test_fstring_17a.txt
new file mode 100644
index 0000000..9eefd91
--- /dev/null
+++ b/tests/snippets/python/test_fstring_17a.txt
@@ -0,0 +1,14 @@
+---input---
+f'x = {x:+3}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'x = ' Literal.String.Single
+'{' Literal.String.Interpol
+'x' Name
+':' Literal.String.Interpol
+'+3' Literal.String.Single
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_17b.txt b/tests/snippets/python/test_fstring_17b.txt
new file mode 100644
index 0000000..2d26481
--- /dev/null
+++ b/tests/snippets/python/test_fstring_17b.txt
@@ -0,0 +1,14 @@
+---input---
+f"x = {x:+3}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'x = ' Literal.String.Double
+'{' Literal.String.Interpol
+'x' Name
+':' Literal.String.Interpol
+'+3' Literal.String.Double
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_18a.txt b/tests/snippets/python/test_fstring_18a.txt
new file mode 100644
index 0000000..5455db7
--- /dev/null
+++ b/tests/snippets/python/test_fstring_18a.txt
@@ -0,0 +1,25 @@
+---input---
+f'{fn(lst,2)} {fn(lst,3)}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'fn' Name
+'(' Punctuation
+'lst' Name
+',' Punctuation
+'2' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+' ' Literal.String.Single
+'{' Literal.String.Interpol
+'fn' Name
+'(' Punctuation
+'lst' Name
+',' Punctuation
+'3' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_18b.txt b/tests/snippets/python/test_fstring_18b.txt
new file mode 100644
index 0000000..356cc3c
--- /dev/null
+++ b/tests/snippets/python/test_fstring_18b.txt
@@ -0,0 +1,25 @@
+---input---
+f"{fn(lst,2)} {fn(lst,3)}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'fn' Name
+'(' Punctuation
+'lst' Name
+',' Punctuation
+'2' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+' ' Literal.String.Double
+'{' Literal.String.Interpol
+'fn' Name
+'(' Punctuation
+'lst' Name
+',' Punctuation
+'3' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_19a.txt b/tests/snippets/python/test_fstring_19a.txt
new file mode 100644
index 0000000..7e7cde0
--- /dev/null
+++ b/tests/snippets/python/test_fstring_19a.txt
@@ -0,0 +1,46 @@
+---input---
+f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))} }'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'mapping is ' Literal.String.Single
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'{' Punctuation
+'a' Name
+':' Punctuation
+'b' Name
+' ' Text.Whitespace
+'for' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'a' Name
+',' Punctuation
+' ' Text.Whitespace
+'b' Name
+')' Punctuation
+' ' Text.Whitespace
+'in' Operator.Word
+' ' Text.Whitespace
+'(' Punctuation
+'(' Punctuation
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+')' Punctuation
+',' Punctuation
+' ' Text.Whitespace
+'(' Punctuation
+'3' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'4' Literal.Number.Integer
+')' Punctuation
+')' Punctuation
+'}' Punctuation
+' ' Text.Whitespace
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_19b.txt b/tests/snippets/python/test_fstring_19b.txt
new file mode 100644
index 0000000..3ae438a
--- /dev/null
+++ b/tests/snippets/python/test_fstring_19b.txt
@@ -0,0 +1,46 @@
+---input---
+f"mapping is { {a:b for (a, b) in ((1, 2), (3, 4))} }"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'mapping is ' Literal.String.Double
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'{' Punctuation
+'a' Name
+':' Punctuation
+'b' Name
+' ' Text.Whitespace
+'for' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'a' Name
+',' Punctuation
+' ' Text.Whitespace
+'b' Name
+')' Punctuation
+' ' Text.Whitespace
+'in' Operator.Word
+' ' Text.Whitespace
+'(' Punctuation
+'(' Punctuation
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+')' Punctuation
+',' Punctuation
+' ' Text.Whitespace
+'(' Punctuation
+'3' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'4' Literal.Number.Integer
+')' Punctuation
+')' Punctuation
+'}' Punctuation
+' ' Text.Whitespace
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_20a.txt b/tests/snippets/python/test_fstring_20a.txt
new file mode 100644
index 0000000..206e436
--- /dev/null
+++ b/tests/snippets/python/test_fstring_20a.txt
@@ -0,0 +1,17 @@
+---input---
+f'a={d["a"]}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'a=' Literal.String.Single
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'"' Literal.String.Double
+'a' Literal.String.Double
+'"' Literal.String.Double
+']' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_20b.txt b/tests/snippets/python/test_fstring_20b.txt
new file mode 100644
index 0000000..fb3acc4
--- /dev/null
+++ b/tests/snippets/python/test_fstring_20b.txt
@@ -0,0 +1,17 @@
+---input---
+f"a={d['a']}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'a=' Literal.String.Double
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+"'" Literal.String.Single
+'a' Literal.String.Single
+"'" Literal.String.Single
+']' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_21a.txt b/tests/snippets/python/test_fstring_21a.txt
new file mode 100644
index 0000000..0107431
--- /dev/null
+++ b/tests/snippets/python/test_fstring_21a.txt
@@ -0,0 +1,15 @@
+---input---
+f'a={d[a]}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'a=' Literal.String.Single
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'a' Name
+']' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_21b.txt b/tests/snippets/python/test_fstring_21b.txt
new file mode 100644
index 0000000..f659d54
--- /dev/null
+++ b/tests/snippets/python/test_fstring_21b.txt
@@ -0,0 +1,15 @@
+---input---
+f"a={d[a]}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'a=' Literal.String.Double
+'{' Literal.String.Interpol
+'d' Name
+'[' Punctuation
+'a' Name
+']' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_22a.txt b/tests/snippets/python/test_fstring_22a.txt
new file mode 100644
index 0000000..a1066e9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_22a.txt
@@ -0,0 +1,14 @@
+---input---
+fr'{header}:\s+'
+
+---tokens---
+'fr' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'header' Name
+'}' Literal.String.Interpol
+':' Literal.String.Single
+'\\' Literal.String.Single
+'s+' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_22b.txt b/tests/snippets/python/test_fstring_22b.txt
new file mode 100644
index 0000000..79cb6b4
--- /dev/null
+++ b/tests/snippets/python/test_fstring_22b.txt
@@ -0,0 +1,14 @@
+---input---
+fr"{header}:\s+"
+
+---tokens---
+'fr' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'header' Name
+'}' Literal.String.Interpol
+':' Literal.String.Double
+'\\' Literal.String.Double
+'s+' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_23a.txt b/tests/snippets/python/test_fstring_23a.txt
new file mode 100644
index 0000000..ef5cbd4
--- /dev/null
+++ b/tests/snippets/python/test_fstring_23a.txt
@@ -0,0 +1,11 @@
+---input---
+f'{a!r}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'a' Name
+'!r}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_23b.txt b/tests/snippets/python/test_fstring_23b.txt
new file mode 100644
index 0000000..572fe04
--- /dev/null
+++ b/tests/snippets/python/test_fstring_23b.txt
@@ -0,0 +1,11 @@
+---input---
+f"{a!r}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'a' Name
+'!r}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_24a.txt b/tests/snippets/python/test_fstring_24a.txt
new file mode 100644
index 0000000..d45385b
--- /dev/null
+++ b/tests/snippets/python/test_fstring_24a.txt
@@ -0,0 +1,23 @@
+---input---
+f'{(lambda x: x*2)(3)}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'(' Punctuation
+'lambda' Keyword
+' ' Text.Whitespace
+'x' Name
+':' Punctuation
+' ' Text.Whitespace
+'x' Name
+'*' Operator
+'2' Literal.Number.Integer
+')' Punctuation
+'(' Punctuation
+'3' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_24b.txt b/tests/snippets/python/test_fstring_24b.txt
new file mode 100644
index 0000000..5e83b19
--- /dev/null
+++ b/tests/snippets/python/test_fstring_24b.txt
@@ -0,0 +1,23 @@
+---input---
+f"{(lambda x: x*2)(3)}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'(' Punctuation
+'lambda' Keyword
+' ' Text.Whitespace
+'x' Name
+':' Punctuation
+' ' Text.Whitespace
+'x' Name
+'*' Operator
+'2' Literal.Number.Integer
+')' Punctuation
+'(' Punctuation
+'3' Literal.Number.Integer
+')' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_25a.txt b/tests/snippets/python/test_fstring_25a.txt
new file mode 100644
index 0000000..470db36
--- /dev/null
+++ b/tests/snippets/python/test_fstring_25a.txt
@@ -0,0 +1,24 @@
+---input---
+extra = f'{extra},waiters:{len(self._waiters)}'
+
+---tokens---
+'extra' Name
+' ' Text
+'=' Operator
+' ' Text
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'extra' Name
+'}' Literal.String.Interpol
+',waiters:' Literal.String.Single
+'{' Literal.String.Interpol
+'len' Name.Builtin
+'(' Punctuation
+'self' Name.Builtin.Pseudo
+'.' Operator
+'_waiters' Name
+')' Punctuation
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_25b.txt b/tests/snippets/python/test_fstring_25b.txt
new file mode 100644
index 0000000..94893e5
--- /dev/null
+++ b/tests/snippets/python/test_fstring_25b.txt
@@ -0,0 +1,24 @@
+---input---
+extra = f"{extra},waiters:{len(self._waiters)}"
+
+---tokens---
+'extra' Name
+' ' Text
+'=' Operator
+' ' Text
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'extra' Name
+'}' Literal.String.Interpol
+',waiters:' Literal.String.Double
+'{' Literal.String.Interpol
+'len' Name.Builtin
+'(' Punctuation
+'self' Name.Builtin.Pseudo
+'.' Operator
+'_waiters' Name
+')' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_26a.txt b/tests/snippets/python/test_fstring_26a.txt
new file mode 100644
index 0000000..01a231d
--- /dev/null
+++ b/tests/snippets/python/test_fstring_26a.txt
@@ -0,0 +1,20 @@
+---input---
+message.append(f" [line {lineno:2d}]")
+
+---tokens---
+'message' Name
+'.' Operator
+'append' Name
+'(' Punctuation
+'f' Literal.String.Affix
+'"' Literal.String.Double
+' [line ' Literal.String.Double
+'{' Literal.String.Interpol
+'lineno' Name
+':' Literal.String.Interpol
+'2d' Literal.String.Double
+'}' Literal.String.Interpol
+']' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_26b.txt b/tests/snippets/python/test_fstring_26b.txt
new file mode 100644
index 0000000..a813149
--- /dev/null
+++ b/tests/snippets/python/test_fstring_26b.txt
@@ -0,0 +1,20 @@
+---input---
+message.append(f' [line {lineno:2d}]')
+
+---tokens---
+'message' Name
+'.' Operator
+'append' Name
+'(' Punctuation
+'f' Literal.String.Affix
+"'" Literal.String.Single
+' [line ' Literal.String.Single
+'{' Literal.String.Interpol
+'lineno' Name
+':' Literal.String.Interpol
+'2d' Literal.String.Single
+'}' Literal.String.Interpol
+']' Literal.String.Single
+"'" Literal.String.Single
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_27a.txt b/tests/snippets/python/test_fstring_27a.txt
new file mode 100644
index 0000000..75af0c1
--- /dev/null
+++ b/tests/snippets/python/test_fstring_27a.txt
@@ -0,0 +1,11 @@
+---input---
+f"{foo=}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'foo' Name
+'=}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_27b.txt b/tests/snippets/python/test_fstring_27b.txt
new file mode 100644
index 0000000..71e6e51
--- /dev/null
+++ b/tests/snippets/python/test_fstring_27b.txt
@@ -0,0 +1,11 @@
+---input---
+f'{foo=}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'foo' Name
+'=}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_28a.txt b/tests/snippets/python/test_fstring_28a.txt
new file mode 100644
index 0000000..cdc65db
--- /dev/null
+++ b/tests/snippets/python/test_fstring_28a.txt
@@ -0,0 +1,11 @@
+---input---
+f'{foo=!s}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'foo' Name
+'=!s}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_28b.txt b/tests/snippets/python/test_fstring_28b.txt
new file mode 100644
index 0000000..99cf4f3
--- /dev/null
+++ b/tests/snippets/python/test_fstring_28b.txt
@@ -0,0 +1,11 @@
+---input---
+f"{foo=!s}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'foo' Name
+'=!s}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_29a.txt b/tests/snippets/python/test_fstring_29a.txt
new file mode 100644
index 0000000..2100b3b
--- /dev/null
+++ b/tests/snippets/python/test_fstring_29a.txt
@@ -0,0 +1,15 @@
+---input---
+f"{math.pi=!f:.2f}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'math' Name
+'.' Operator
+'pi' Name
+'=!f:' Literal.String.Interpol
+'.2f' Literal.String.Double
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_29b.txt b/tests/snippets/python/test_fstring_29b.txt
new file mode 100644
index 0000000..4cc18dd
--- /dev/null
+++ b/tests/snippets/python/test_fstring_29b.txt
@@ -0,0 +1,15 @@
+---input---
+f'{math.pi=!f:.2f}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'math' Name
+'.' Operator
+'pi' Name
+'=!f:' Literal.String.Interpol
+'.2f' Literal.String.Single
+'}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_30a.txt b/tests/snippets/python/test_fstring_30a.txt
new file mode 100644
index 0000000..3db443e
--- /dev/null
+++ b/tests/snippets/python/test_fstring_30a.txt
@@ -0,0 +1,16 @@
+---input---
+f"{ chr(65) =}"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'chr' Name.Builtin
+'(' Punctuation
+'65' Literal.Number.Integer
+')' Punctuation
+' ' Text.Whitespace
+'=}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_30b.txt b/tests/snippets/python/test_fstring_30b.txt
new file mode 100644
index 0000000..5f082e5
--- /dev/null
+++ b/tests/snippets/python/test_fstring_30b.txt
@@ -0,0 +1,16 @@
+---input---
+f'{ chr(65) =}'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'chr' Name.Builtin
+'(' Punctuation
+'65' Literal.Number.Integer
+')' Punctuation
+' ' Text.Whitespace
+'=}' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_31a.txt b/tests/snippets/python/test_fstring_31a.txt
new file mode 100644
index 0000000..3e9c091
--- /dev/null
+++ b/tests/snippets/python/test_fstring_31a.txt
@@ -0,0 +1,15 @@
+---input---
+f"{chr(65) = }"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'chr' Name.Builtin
+'(' Punctuation
+'65' Literal.Number.Integer
+')' Punctuation
+' ' Text.Whitespace
+'= }' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_31b.txt b/tests/snippets/python/test_fstring_31b.txt
new file mode 100644
index 0000000..37adb3e
--- /dev/null
+++ b/tests/snippets/python/test_fstring_31b.txt
@@ -0,0 +1,15 @@
+---input---
+f'{chr(65) = }'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'chr' Name.Builtin
+'(' Punctuation
+'65' Literal.Number.Integer
+')' Punctuation
+' ' Text.Whitespace
+'= }' Literal.String.Interpol
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_32a.txt b/tests/snippets/python/test_fstring_32a.txt
new file mode 100644
index 0000000..dcfc39d
--- /dev/null
+++ b/tests/snippets/python/test_fstring_32a.txt
@@ -0,0 +1,15 @@
+---input---
+f'*{n=:30}*'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'*' Literal.String.Single
+'{' Literal.String.Interpol
+'n' Name
+'=:' Literal.String.Interpol
+'30' Literal.String.Single
+'}' Literal.String.Interpol
+'*' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_32b.txt b/tests/snippets/python/test_fstring_32b.txt
new file mode 100644
index 0000000..1f8a450
--- /dev/null
+++ b/tests/snippets/python/test_fstring_32b.txt
@@ -0,0 +1,15 @@
+---input---
+f"*{n=:30}*"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'*' Literal.String.Double
+'{' Literal.String.Interpol
+'n' Name
+'=:' Literal.String.Interpol
+'30' Literal.String.Double
+'}' Literal.String.Interpol
+'*' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_33a.txt b/tests/snippets/python/test_fstring_33a.txt
new file mode 100644
index 0000000..5318b55
--- /dev/null
+++ b/tests/snippets/python/test_fstring_33a.txt
@@ -0,0 +1,15 @@
+---input---
+f'*{n=!r:30}*'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'*' Literal.String.Single
+'{' Literal.String.Interpol
+'n' Name
+'=!r:' Literal.String.Interpol
+'30' Literal.String.Single
+'}' Literal.String.Interpol
+'*' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_33b.txt b/tests/snippets/python/test_fstring_33b.txt
new file mode 100644
index 0000000..a0211dd
--- /dev/null
+++ b/tests/snippets/python/test_fstring_33b.txt
@@ -0,0 +1,15 @@
+---input---
+f"*{n=!r:30}*"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'*' Literal.String.Double
+'{' Literal.String.Interpol
+'n' Name
+'=!r:' Literal.String.Interpol
+'30' Literal.String.Double
+'}' Literal.String.Interpol
+'*' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_34a.txt b/tests/snippets/python/test_fstring_34a.txt
new file mode 100644
index 0000000..9b80cc9
--- /dev/null
+++ b/tests/snippets/python/test_fstring_34a.txt
@@ -0,0 +1,20 @@
+---input---
+f"*{f'{n=}':30}*"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'*' Literal.String.Double
+'{' Literal.String.Interpol
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'{' Literal.String.Interpol
+'n' Name
+'=}' Literal.String.Interpol
+"'" Literal.String.Single
+':' Literal.String.Interpol
+'30' Literal.String.Double
+'}' Literal.String.Interpol
+'*' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_34b.txt b/tests/snippets/python/test_fstring_34b.txt
new file mode 100644
index 0000000..5abf37f
--- /dev/null
+++ b/tests/snippets/python/test_fstring_34b.txt
@@ -0,0 +1,20 @@
+---input---
+f'*{f"{n=}":30}*'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'*' Literal.String.Single
+'{' Literal.String.Interpol
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'{' Literal.String.Interpol
+'n' Name
+'=}' Literal.String.Interpol
+'"' Literal.String.Double
+':' Literal.String.Interpol
+'30' Literal.String.Single
+'}' Literal.String.Interpol
+'*' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_35a.txt b/tests/snippets/python/test_fstring_35a.txt
new file mode 100644
index 0000000..b4e1041
--- /dev/null
+++ b/tests/snippets/python/test_fstring_35a.txt
@@ -0,0 +1,15 @@
+---input---
+f'*{n=:+<30}*'
+
+---tokens---
+'f' Literal.String.Affix
+"'" Literal.String.Single
+'*' Literal.String.Single
+'{' Literal.String.Interpol
+'n' Name
+'=:' Literal.String.Interpol
+'+<30' Literal.String.Single
+'}' Literal.String.Interpol
+'*' Literal.String.Single
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_35b.txt b/tests/snippets/python/test_fstring_35b.txt
new file mode 100644
index 0000000..773e7a4
--- /dev/null
+++ b/tests/snippets/python/test_fstring_35b.txt
@@ -0,0 +1,15 @@
+---input---
+f"*{n=:+<30}*"
+
+---tokens---
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'*' Literal.String.Double
+'{' Literal.String.Interpol
+'n' Name
+'=:' Literal.String.Interpol
+'+<30' Literal.String.Double
+'}' Literal.String.Interpol
+'*' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_36a.txt b/tests/snippets/python/test_fstring_36a.txt
new file mode 100644
index 0000000..773e38e
--- /dev/null
+++ b/tests/snippets/python/test_fstring_36a.txt
@@ -0,0 +1,16 @@
+---input---
+
+f'''{foo
+ = !s:20}'''
+
+---tokens---
+'f' Literal.String.Affix
+"'''" Literal.String.Single
+'{' Literal.String.Interpol
+'foo' Name
+'\n ' Text.Whitespace
+'= !s:' Literal.String.Interpol
+'20' Literal.String.Single
+'}' Literal.String.Interpol
+"'''" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_fstring_36b.txt b/tests/snippets/python/test_fstring_36b.txt
new file mode 100644
index 0000000..da79ce2
--- /dev/null
+++ b/tests/snippets/python/test_fstring_36b.txt
@@ -0,0 +1,16 @@
+---input---
+
+f"""{foo
+ = !s:20}"""
+
+---tokens---
+'f' Literal.String.Affix
+'"""' Literal.String.Double
+'{' Literal.String.Interpol
+'foo' Name
+'\n ' Text.Whitespace
+'= !s:' Literal.String.Interpol
+'20' Literal.String.Double
+'}' Literal.String.Interpol
+'"""' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_needs_name.txt b/tests/snippets/python/test_needs_name.txt
new file mode 100644
index 0000000..f121da0
--- /dev/null
+++ b/tests/snippets/python/test_needs_name.txt
@@ -0,0 +1,55 @@
+# Tests that '@' is recognized as an Operator
+
+---input---
+S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
+
+---tokens---
+'S' Name
+' ' Text
+'=' Operator
+' ' Text
+'(' Punctuation
+'H' Name
+' ' Text
+'@' Operator
+' ' Text
+'beta' Name
+' ' Text
+'-' Operator
+' ' Text
+'r' Name
+')' Punctuation
+'.' Operator
+'T' Name
+' ' Text
+'@' Operator
+' ' Text
+'inv' Name
+'(' Punctuation
+'H' Name
+' ' Text
+'@' Operator
+' ' Text
+'V' Name
+' ' Text
+'@' Operator
+' ' Text
+'H' Name
+'.' Operator
+'T' Name
+')' Punctuation
+' ' Text
+'@' Operator
+' ' Text
+'(' Punctuation
+'H' Name
+' ' Text
+'@' Operator
+' ' Text
+'beta' Name
+' ' Text
+'-' Operator
+' ' Text
+'r' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_pep_515.txt b/tests/snippets/python/test_pep_515.txt
new file mode 100644
index 0000000..38fa619
--- /dev/null
+++ b/tests/snippets/python/test_pep_515.txt
@@ -0,0 +1,28 @@
+# Tests that the lexer can parse numeric literals with underscores
+
+---input---
+1_000_000
+1_000.000_001
+1_000e1_000j
+0xCAFE_F00D
+0b_0011_1111_0100_1110
+0o_777_123
+
+---tokens---
+'1_000_000' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'1_000.000_001' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1_000e1_000j' Literal.Number.Float
+'\n' Text.Whitespace
+
+'0xCAFE_F00D' Literal.Number.Hex
+'\n' Text.Whitespace
+
+'0b_0011_1111_0100_1110' Literal.Number.Bin
+'\n' Text.Whitespace
+
+'0o_777_123' Literal.Number.Oct
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_raw_fstring.txt b/tests/snippets/python/test_raw_fstring.txt
new file mode 100644
index 0000000..3381088
--- /dev/null
+++ b/tests/snippets/python/test_raw_fstring.txt
@@ -0,0 +1,46 @@
+# Tests that the lexer can parse raw f-strings
+
+---input---
+rf"m_\nu = x"
+
+f"m_\nu = {x}"
+
+rf"m_{{\nu}} = {x}"
+
+---tokens---
+'rf' Literal.String.Affix
+'"' Literal.String.Double
+'m_' Literal.String.Double
+'\\' Literal.String.Double
+'nu = x' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'f' Literal.String.Affix
+'"' Literal.String.Double
+'m_' Literal.String.Double
+'\\n' Literal.String.Escape
+'u = ' Literal.String.Double
+'{' Literal.String.Interpol
+'x' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'rf' Literal.String.Affix
+'"' Literal.String.Double
+'m_' Literal.String.Double
+'{{' Literal.String.Escape
+'\\' Literal.String.Double
+'nu' Literal.String.Double
+'}}' Literal.String.Escape
+' = ' Literal.String.Double
+'{' Literal.String.Interpol
+'x' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_string_escape_codes.txt b/tests/snippets/python/test_string_escape_codes.txt
new file mode 100644
index 0000000..c02dc03
--- /dev/null
+++ b/tests/snippets/python/test_string_escape_codes.txt
@@ -0,0 +1,20 @@
+---input---
+'\\ \n \x12 \777 \u1234 \U00010348 \N{Plus-Minus Sign}'
+
+---tokens---
+"'" Literal.String.Single
+'\\\\' Literal.String.Escape
+' ' Literal.String.Single
+'\\n' Literal.String.Escape
+' ' Literal.String.Single
+'\\x12' Literal.String.Escape
+' ' Literal.String.Single
+'\\777' Literal.String.Escape
+' ' Literal.String.Single
+'\\u1234' Literal.String.Escape
+' ' Literal.String.Single
+'\\U00010348' Literal.String.Escape
+' ' Literal.String.Single
+'\\N{Plus-Minus Sign}' Literal.String.Escape
+"'" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/python/test_walrus_operator.txt b/tests/snippets/python/test_walrus_operator.txt
new file mode 100644
index 0000000..9bab89d
--- /dev/null
+++ b/tests/snippets/python/test_walrus_operator.txt
@@ -0,0 +1,21 @@
+# Tests that ':=' is recognized as an Operator
+
+---input---
+if (a := 2) > 4:
+
+---tokens---
+'if' Keyword
+' ' Text
+'(' Punctuation
+'a' Name
+' ' Text
+':=' Operator
+' ' Text
+'2' Literal.Number.Integer
+')' Punctuation
+' ' Text
+'>' Operator
+' ' Text
+'4' Literal.Number.Integer
+':' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/python2/test_cls_builtin.txt b/tests/snippets/python2/test_cls_builtin.txt
new file mode 100644
index 0000000..ff533c2
--- /dev/null
+++ b/tests/snippets/python2/test_cls_builtin.txt
@@ -0,0 +1,34 @@
+# Tests that a cls token gets interpreted as a Token.Name.Builtin.Pseudo
+
+---input---
+class TestClass():
+ @classmethod
+ def hello(cls):
+ pass
+
+---tokens---
+'class' Keyword
+' ' Text
+'TestClass' Name.Class
+'(' Punctuation
+')' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text
+'@classmethod' Name.Decorator
+'\n' Text.Whitespace
+
+' ' Text
+'def' Keyword
+' ' Text
+'hello' Name.Function
+'(' Punctuation
+'cls' Name.Builtin.Pseudo
+')' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+' ' Text
+'pass' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/qbasic/test_keywords_with_dollar.txt b/tests/snippets/qbasic/test_keywords_with_dollar.txt
new file mode 100644
index 0000000..21c4fba
--- /dev/null
+++ b/tests/snippets/qbasic/test_keywords_with_dollar.txt
@@ -0,0 +1,22 @@
+---input---
+DIM x
+x = RIGHT$("abc", 1)
+
+---tokens---
+'DIM' Keyword.Declaration
+' ' Text.Whitespace
+'x' Name.Variable.Global
+'\n' Text
+
+'x' Name.Variable.Global
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'RIGHT$' Keyword.Reserved
+'(' Punctuation
+'"abc"' Literal.String.Double
+',' Punctuation
+' ' Text.Whitespace
+'1' Literal.Number.Integer.Long
+')' Punctuation
+'\n' Text
diff --git a/tests/snippets/r/test_call.txt b/tests/snippets/r/test_call.txt
new file mode 100644
index 0000000..c35a71e
--- /dev/null
+++ b/tests/snippets/r/test_call.txt
@@ -0,0 +1,12 @@
+---input---
+f(1, a)
+
+---tokens---
+'f' Name.Function
+'(' Punctuation
+'1' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'a' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_custom_operator.txt b/tests/snippets/r/test_custom_operator.txt
new file mode 100644
index 0000000..1b19df7
--- /dev/null
+++ b/tests/snippets/r/test_custom_operator.txt
@@ -0,0 +1,10 @@
+---input---
+7 % and % 8
+
+---tokens---
+'7' Literal.Number
+' ' Text.Whitespace
+'% and %' Operator
+' ' Text.Whitespace
+'8' Literal.Number
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_dot_indexing.txt b/tests/snippets/r/test_dot_indexing.txt
new file mode 100644
index 0000000..ee0871f
--- /dev/null
+++ b/tests/snippets/r/test_dot_indexing.txt
@@ -0,0 +1,9 @@
+---input---
+.[1]
+
+---tokens---
+'.' Name
+'[' Punctuation
+'1' Literal.Number
+']' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_dot_name.txt b/tests/snippets/r/test_dot_name.txt
new file mode 100644
index 0000000..01df528
--- /dev/null
+++ b/tests/snippets/r/test_dot_name.txt
@@ -0,0 +1,10 @@
+---input---
+. <- 1
+
+---tokens---
+'.' Name
+' ' Text.Whitespace
+'<-' Operator
+' ' Text.Whitespace
+'1' Literal.Number
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_indexing.txt b/tests/snippets/r/test_indexing.txt
new file mode 100644
index 0000000..6491d1a
--- /dev/null
+++ b/tests/snippets/r/test_indexing.txt
@@ -0,0 +1,9 @@
+---input---
+a[1]
+
+---tokens---
+'a' Name
+'[' Punctuation
+'1' Literal.Number
+']' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_name1.txt b/tests/snippets/r/test_name1.txt
new file mode 100644
index 0000000..a7651cb
--- /dev/null
+++ b/tests/snippets/r/test_name1.txt
@@ -0,0 +1,6 @@
+---input---
+._a_2.c
+
+---tokens---
+'._a_2.c' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_name2.txt b/tests/snippets/r/test_name2.txt
new file mode 100644
index 0000000..1570aec
--- /dev/null
+++ b/tests/snippets/r/test_name2.txt
@@ -0,0 +1,8 @@
+# Invalid names are valid if backticks are used
+
+---input---
+`.1 blah`
+
+---tokens---
+'`.1 blah`' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/r/test_name3.txt b/tests/snippets/r/test_name3.txt
new file mode 100644
index 0000000..fe5e8a4
--- /dev/null
+++ b/tests/snippets/r/test_name3.txt
@@ -0,0 +1,8 @@
+# Internal backticks can be escaped
+
+---input---
+`.1 \` blah`
+
+---tokens---
+'`.1 \\` blah`' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_escaped_bracestring.txt b/tests/snippets/ruby/test_escaped_bracestring.txt
new file mode 100644
index 0000000..14718b9
--- /dev/null
+++ b/tests/snippets/ruby/test_escaped_bracestring.txt
@@ -0,0 +1,19 @@
+---input---
+str.gsub(%r{\\\\}, "/")
+
+---tokens---
+'str' Name
+'.' Operator
+'gsub' Name
+'(' Punctuation
+'%r{' Literal.String.Regex
+'\\\\' Literal.String.Regex
+'\\\\' Literal.String.Regex
+'}' Literal.String.Regex
+',' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'/' Literal.String.Double
+'"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_interpolation_nested_curly.txt b/tests/snippets/ruby/test_interpolation_nested_curly.txt
new file mode 100644
index 0000000..f4a69f7
--- /dev/null
+++ b/tests/snippets/ruby/test_interpolation_nested_curly.txt
@@ -0,0 +1,56 @@
+---input---
+"A#{ (3..5).group_by { |x| x/2}.map do |k,v| "#{k}" end.join }" + "Z"
+
+---tokens---
+'"' Literal.String.Double
+'A' Literal.String.Double
+'#{' Literal.String.Interpol
+' ' Text.Whitespace
+'(' Punctuation
+'3' Literal.Number.Integer
+'..' Operator
+'5' Literal.Number.Integer
+')' Punctuation
+'.' Operator
+'group_by' Name
+' ' Text.Whitespace
+'{' Literal.String.Interpol
+' ' Text.Whitespace
+'|' Operator
+'x' Name
+'|' Operator
+' ' Text.Whitespace
+'x' Name
+'/' Operator
+'2' Literal.Number.Integer
+'}' Literal.String.Interpol
+'.' Operator
+'map' Name
+' ' Text.Whitespace
+'do' Keyword
+' ' Text.Whitespace
+'|' Operator
+'k' Name
+',' Punctuation
+'v' Name
+'|' Operator
+' ' Text.Whitespace
+'"' Literal.String.Double
+'#{' Literal.String.Interpol
+'k' Name
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text.Whitespace
+'end' Keyword
+'.' Operator
+'join' Name
+' ' Text.Whitespace
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'"' Literal.String.Double
+'Z' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_operator_methods.txt b/tests/snippets/ruby/test_operator_methods.txt
new file mode 100644
index 0000000..c8f7a7a
--- /dev/null
+++ b/tests/snippets/ruby/test_operator_methods.txt
@@ -0,0 +1,9 @@
+---input---
+x.==4
+
+---tokens---
+'x' Name
+'.' Operator
+'==' Name.Operator
+'4' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_range_syntax1.txt b/tests/snippets/ruby/test_range_syntax1.txt
new file mode 100644
index 0000000..f0fc15d
--- /dev/null
+++ b/tests/snippets/ruby/test_range_syntax1.txt
@@ -0,0 +1,8 @@
+---input---
+1..3
+
+---tokens---
+'1' Literal.Number.Integer
+'..' Operator
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_range_syntax2.txt b/tests/snippets/ruby/test_range_syntax2.txt
new file mode 100644
index 0000000..a3ba24a
--- /dev/null
+++ b/tests/snippets/ruby/test_range_syntax2.txt
@@ -0,0 +1,8 @@
+---input---
+1...3
+
+---tokens---
+'1' Literal.Number.Integer
+'...' Operator
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/ruby/test_range_syntax3.txt b/tests/snippets/ruby/test_range_syntax3.txt
new file mode 100644
index 0000000..08bf4b1
--- /dev/null
+++ b/tests/snippets/ruby/test_range_syntax3.txt
@@ -0,0 +1,10 @@
+---input---
+1 .. 3
+
+---tokens---
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+'..' Operator
+' ' Text.Whitespace
+'3' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/rust/test_attribute.txt b/tests/snippets/rust/test_attribute.txt
new file mode 100644
index 0000000..2c4a889
--- /dev/null
+++ b/tests/snippets/rust/test_attribute.txt
@@ -0,0 +1,12 @@
+---input---
+#[foo(bar = [baz, qux])]
+
+---tokens---
+'#[' Comment.Preproc
+'foo(bar = ' Comment.Preproc
+'[' Comment.Preproc
+'baz, qux' Comment.Preproc
+']' Comment.Preproc
+')' Comment.Preproc
+']' Comment.Preproc
+'\n' Text.Whitespace
diff --git a/tests/snippets/rust/test_break.txt b/tests/snippets/rust/test_break.txt
new file mode 100644
index 0000000..7dafde2
--- /dev/null
+++ b/tests/snippets/rust/test_break.txt
@@ -0,0 +1,39 @@
+---input---
+loop {
+ break;
+ break 'foo;
+ break'foo;
+ break_it;
+}
+
+---tokens---
+'loop' Keyword
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'break' Keyword
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'break' Keyword
+' ' Text.Whitespace
+"'foo" Name.Label
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'break' Keyword
+"'foo" Name.Label
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'break_it' Name
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/rust/test_rawstrings.txt b/tests/snippets/rust/test_rawstrings.txt
new file mode 100644
index 0000000..69524f9
--- /dev/null
+++ b/tests/snippets/rust/test_rawstrings.txt
@@ -0,0 +1,117 @@
+---input---
+fn main() {
+ let raw_str = r"Escapes don't work
+
+ here: \x3F \u{211D}";
+ println!("{}", raw_str);
+
+ // If you need quotes in a raw string, add a pair of #s
+ let quotes = r#"And then I said:
+
+ "There is no escape!""#;
+ println!("{}", quotes);
+
+ // If you need "# in your string, just use more #s in the delimiter.
+ // There is no limit for the number of #s you can use.
+ let longer_delimiter = r###"A string
+ with "# in it. And even "##!"###;
+ println!("{}", longer_delimiter);
+}
+
+---tokens---
+'fn' Keyword
+' ' Text
+'main' Name.Function
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'let' Keyword.Declaration
+' ' Text.Whitespace
+'raw_str' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'r"Escapes don\'t work\n\n here: \\x3F \\u{211D}"' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'println!' Name.Function.Magic
+'(' Punctuation
+'"' Literal.String
+'{}' Literal.String
+'"' Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'raw_str' Name
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'// If you need quotes in a raw string, add a pair of #s\n' Comment.Single
+
+' ' Text.Whitespace
+'let' Keyword.Declaration
+' ' Text.Whitespace
+'quotes' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'r#"And then I said:\n\n "There is no escape!""#' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'println!' Name.Function.Magic
+'(' Punctuation
+'"' Literal.String
+'{}' Literal.String
+'"' Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'quotes' Name
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'// If you need "# in your string, just use more #s in the delimiter.\n' Comment.Single
+
+' ' Text.Whitespace
+'// There is no limit for the number of #s you can use.\n' Comment.Single
+
+' ' Text.Whitespace
+'let' Keyword.Declaration
+' ' Text.Whitespace
+'longer_delimiter' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'r###"A string\n with "# in it. And even "##!"###' Literal.String
+';' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'println!' Name.Function.Magic
+'(' Punctuation
+'"' Literal.String
+'{}' Literal.String
+'"' Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'longer_delimiter' Name
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_colon_colon_function_name.txt b/tests/snippets/scala/test_colon_colon_function_name.txt
new file mode 100644
index 0000000..8840fff
--- /dev/null
+++ b/tests/snippets/scala/test_colon_colon_function_name.txt
@@ -0,0 +1,33 @@
+---input---
+def ::(xs: List[T]): List[T] = ::(x, xs)
+
+---tokens---
+'def' Keyword
+' ' Text.Whitespace
+'::' Name.Function
+'(' Punctuation
+'xs' Name
+':' Punctuation
+' ' Text.Whitespace
+'List' Name.Class
+'[' Punctuation
+'T' Name.Class
+']' Punctuation
+')' Punctuation
+':' Punctuation
+' ' Text.Whitespace
+'List' Name.Class
+'[' Punctuation
+'T' Name.Class
+']' Punctuation
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'::' Name
+'(' Punctuation
+'x' Name
+',' Punctuation
+' ' Text.Whitespace
+'xs' Name
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_default_parameter.txt b/tests/snippets/scala/test_default_parameter.txt
new file mode 100644
index 0000000..16ce948
--- /dev/null
+++ b/tests/snippets/scala/test_default_parameter.txt
@@ -0,0 +1,37 @@
+---input---
+def f(using y: Char = if true then 'a' else 2): Int = ???
+
+---tokens---
+'def' Keyword
+' ' Text.Whitespace
+'f' Name.Function
+'(' Punctuation
+'using' Keyword
+' ' Text.Whitespace
+'y' Name
+':' Punctuation
+' ' Text.Whitespace
+'Char' Name.Class
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'if' Keyword
+' ' Text.Whitespace
+'true' Keyword.Constant
+' ' Text.Whitespace
+'then' Keyword
+' ' Text.Whitespace
+"'a'" Literal.String.Char
+' ' Text.Whitespace
+'else' Keyword
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+')' Punctuation
+':' Punctuation
+' ' Text.Whitespace
+'Int' Name.Class
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'???' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_end_val.txt b/tests/snippets/scala/test_end_val.txt
new file mode 100644
index 0000000..43ac172
--- /dev/null
+++ b/tests/snippets/scala/test_end_val.txt
@@ -0,0 +1,8 @@
+---input---
+end val
+
+---tokens---
+'end' Keyword
+' ' Text.Whitespace
+'val' Keyword
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_end_valx.txt b/tests/snippets/scala/test_end_valx.txt
new file mode 100644
index 0000000..e1d17d7
--- /dev/null
+++ b/tests/snippets/scala/test_end_valx.txt
@@ -0,0 +1,8 @@
+---input---
+end valx
+
+---tokens---
+'end' Keyword
+' ' Text.Whitespace
+'valx' Name.Namespace
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_float_with_exponents.txt b/tests/snippets/scala/test_float_with_exponents.txt
new file mode 100644
index 0000000..d64ee63
--- /dev/null
+++ b/tests/snippets/scala/test_float_with_exponents.txt
@@ -0,0 +1,12 @@
+---input---
+.1e12 .1e+34 .1e-56 .1e12f
+
+---tokens---
+'.1e12' Literal.Number.Float
+' ' Text.Whitespace
+'.1e+34' Literal.Number.Float
+' ' Text.Whitespace
+'.1e-56' Literal.Number.Float
+' ' Text.Whitespace
+'.1e12f' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_function_operator_name.txt b/tests/snippets/scala/test_function_operator_name.txt
new file mode 100644
index 0000000..72c375d
--- /dev/null
+++ b/tests/snippets/scala/test_function_operator_name.txt
@@ -0,0 +1,18 @@
+---input---
+def < (y: String): Boolean
+
+---tokens---
+'def' Keyword
+' ' Text.Whitespace
+'<' Name.Function
+' ' Text.Whitespace
+'(' Punctuation
+'y' Name
+':' Punctuation
+' ' Text.Whitespace
+'String' Name.Class
+')' Punctuation
+':' Punctuation
+' ' Text.Whitespace
+'Boolean' Name.Class
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_import_path.txt b/tests/snippets/scala/test_import_path.txt
new file mode 100644
index 0000000..1c316a0
--- /dev/null
+++ b/tests/snippets/scala/test_import_path.txt
@@ -0,0 +1,12 @@
+---input---
+import a.b.c
+
+---tokens---
+'import' Keyword
+' ' Text.Whitespace
+'a' Name.Namespace
+'.' Punctuation
+'b' Name.Namespace
+'.' Punctuation
+'c' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_invalid_symbol_and_invalid_char.txt b/tests/snippets/scala/test_invalid_symbol_and_invalid_char.txt
new file mode 100644
index 0000000..ccaae59
--- /dev/null
+++ b/tests/snippets/scala/test_invalid_symbol_and_invalid_char.txt
@@ -0,0 +1,8 @@
+---input---
+'1 //'
+
+---tokens---
+"'" Error
+'1' Literal.Number.Integer
+' ' Text.Whitespace
+"//'\n" Comment.Single
diff --git a/tests/snippets/scala/test_open_soft_keyword.txt b/tests/snippets/scala/test_open_soft_keyword.txt
new file mode 100644
index 0000000..903be71
--- /dev/null
+++ b/tests/snippets/scala/test_open_soft_keyword.txt
@@ -0,0 +1,12 @@
+---input---
+val open = true
+
+---tokens---
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'open' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'true' Keyword.Constant
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_package_name.txt b/tests/snippets/scala/test_package_name.txt
new file mode 100644
index 0000000..53633e2
--- /dev/null
+++ b/tests/snippets/scala/test_package_name.txt
@@ -0,0 +1,11 @@
+---input---
+package p1.p2:
+
+---tokens---
+'package' Keyword
+' ' Text.Whitespace
+'p1' Name.Namespace
+'.' Punctuation
+'p2' Name
+':' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_prepend_operator.txt b/tests/snippets/scala/test_prepend_operator.txt
new file mode 100644
index 0000000..cf7b2f9
--- /dev/null
+++ b/tests/snippets/scala/test_prepend_operator.txt
@@ -0,0 +1,10 @@
+---input---
+a +: b
+
+---tokens---
+'a' Name
+' ' Text.Whitespace
+'+:' Operator
+' ' Text.Whitespace
+'b' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_qualified_name.txt b/tests/snippets/scala/test_qualified_name.txt
new file mode 100644
index 0000000..a004410
--- /dev/null
+++ b/tests/snippets/scala/test_qualified_name.txt
@@ -0,0 +1,10 @@
+---input---
+a.b.c
+
+---tokens---
+'a' Name
+'.' Punctuation
+'b' Name
+'.' Punctuation
+'c' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_qualified_name_class.txt b/tests/snippets/scala/test_qualified_name_class.txt
new file mode 100644
index 0000000..7345092
--- /dev/null
+++ b/tests/snippets/scala/test_qualified_name_class.txt
@@ -0,0 +1,10 @@
+---input---
+a.b.C
+
+---tokens---
+'a' Name
+'.' Punctuation
+'b' Name
+'.' Punctuation
+'C' Name.Class
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_script_header.txt b/tests/snippets/scala/test_script_header.txt
new file mode 100644
index 0000000..6d96fd6
--- /dev/null
+++ b/tests/snippets/scala/test_script_header.txt
@@ -0,0 +1,6 @@
+---input---
+#!/usr/bin/scala
+
+---tokens---
+'#!/usr/bin/scala' Comment.Hashbang
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_symbol_followed_by_op.txt b/tests/snippets/scala/test_symbol_followed_by_op.txt
new file mode 100644
index 0000000..518abce
--- /dev/null
+++ b/tests/snippets/scala/test_symbol_followed_by_op.txt
@@ -0,0 +1,7 @@
+---input---
+symbol*
+
+---tokens---
+'symbol' Name
+'*' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_symbol_name_ending_with_star.txt b/tests/snippets/scala/test_symbol_name_ending_with_star.txt
new file mode 100644
index 0000000..25e5dc6
--- /dev/null
+++ b/tests/snippets/scala/test_symbol_name_ending_with_star.txt
@@ -0,0 +1,6 @@
+---input---
+symbol_*
+
+---tokens---
+'symbol_*' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/scala/test_underscore_name.txt b/tests/snippets/scala/test_underscore_name.txt
new file mode 100644
index 0000000..f7a24af
--- /dev/null
+++ b/tests/snippets/scala/test_underscore_name.txt
@@ -0,0 +1,12 @@
+---input---
+val head = _head
+
+---tokens---
+'val' Keyword.Declaration
+' ' Text.Whitespace
+'head' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'_head' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/scheme/keywords.txt b/tests/snippets/scheme/keywords.txt
new file mode 100644
index 0000000..046a444
--- /dev/null
+++ b/tests/snippets/scheme/keywords.txt
@@ -0,0 +1,43 @@
+---input---
+(define* (foo #:key (bar123? 'baz))
+ (display bar123?)
+ (newline))
+
+(foo #:bar123? 'xyz)
+
+---tokens---
+'(' Punctuation
+'define*' Keyword
+' ' Text.Whitespace
+'(' Punctuation
+'foo' Name.Function
+' ' Text.Whitespace
+'#:key' Keyword.Declaration
+' ' Text.Whitespace
+'(' Punctuation
+'bar123?' Name.Function
+' ' Text.Whitespace
+"'baz" Literal.String.Symbol
+')' Punctuation
+')' Punctuation
+'\n ' Text.Whitespace
+'(' Punctuation
+'display' Name.Builtin
+' ' Text.Whitespace
+'bar123?' Name.Variable
+')' Punctuation
+'\n ' Text.Whitespace
+'(' Punctuation
+'newline' Name.Builtin
+')' Punctuation
+')' Punctuation
+'\n\n' Text.Whitespace
+
+'(' Punctuation
+'foo' Name.Function
+' ' Text.Whitespace
+'#:bar123?' Keyword.Declaration
+' ' Text.Whitespace
+"'xyz" Literal.String.Symbol
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/scheme/numbers.txt b/tests/snippets/scheme/numbers.txt
new file mode 100644
index 0000000..f03b3a7
--- /dev/null
+++ b/tests/snippets/scheme/numbers.txt
@@ -0,0 +1,169 @@
+---input---
+;; All sorts of numbers, common and less common.
+
+0
+142
+-142
++142
+-0.5
++0.5
+-0.5e-10
++0.5e10
+0.5e+10
+.5F+10
+.123
+123.
+123.L-25|54
++inf.0
+-inf.0
+#b+nan.0-inf.0i
+1-2i
+1-i
++i
+-5f24@3.14159
+4/5
+5.4e5@4
+#i5
+#o5
+#i#o5
+#o#i5
+#b01/10
+#x0f42a
+#E#b01/10
+#d#I01/10
+#i-324@3.14159
+#o#I01/1022-inf.0i
+
+;; These are not numbers but plain symbols.
+
+1+
+1-
+i
+inf
+-inf
+nan
+-nan
+
+---tokens---
+';; All sorts of numbers, common and less common.' Comment.Single
+'\n\n' Text.Whitespace
+
+'0' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'142' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'-142' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'+142' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'-0.5' Literal.Number.Float
+'\n' Text.Whitespace
+
+'+0.5' Literal.Number.Float
+'\n' Text.Whitespace
+
+'-0.5e-10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'+0.5e10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'0.5e+10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'.5F+10' Literal.Number.Float
+'\n' Text.Whitespace
+
+'.123' Literal.Number.Float
+'\n' Text.Whitespace
+
+'123.' Literal.Number.Float
+'\n' Text.Whitespace
+
+'123.L-25|54' Literal.Number.Float
+'\n' Text.Whitespace
+
+'+inf.0' Literal.Number.Float
+'\n' Text.Whitespace
+
+'-inf.0' Literal.Number.Float
+'\n' Text.Whitespace
+
+'#b+nan.0-inf.0i' Literal.Number.Bin
+'\n' Text.Whitespace
+
+'1-2i' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'1-i' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'+i' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'-5f24@3.14159' Literal.Number.Float
+'\n' Text.Whitespace
+
+'4/5' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'5.4e5@4' Literal.Number.Float
+'\n' Text.Whitespace
+
+'#i5' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'#o5' Literal.Number.Oct
+'\n' Text.Whitespace
+
+'#i#o5' Literal.Number.Oct
+'\n' Text.Whitespace
+
+'#o#i5' Literal.Number.Oct
+'\n' Text.Whitespace
+
+'#b01/10' Literal.Number.Bin
+'\n' Text.Whitespace
+
+'#x0f42a' Literal.Number.Hex
+'\n' Text.Whitespace
+
+'#E#b01/10' Literal.Number.Bin
+'\n' Text.Whitespace
+
+'#d#I01/10' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'#i-324@3.14159' Literal.Number.Float
+'\n' Text.Whitespace
+
+'#o#I01/1022-inf.0i' Literal.Number.Oct
+'\n\n' Text.Whitespace
+
+';; These are not numbers but plain symbols.' Comment.Single
+'\n\n' Text.Whitespace
+
+'1+' Name.Builtin
+'\n' Text.Whitespace
+
+'1-' Name.Builtin
+'\n' Text.Whitespace
+
+'i' Name.Variable
+'\n' Text.Whitespace
+
+'inf' Name.Builtin
+'\n' Text.Whitespace
+
+'-inf' Name.Variable
+'\n' Text.Whitespace
+
+'nan' Name.Builtin
+'\n' Text.Whitespace
+
+'-nan' Name.Variable
+'\n' Text.Whitespace
diff --git a/tests/snippets/scheme/strings.txt b/tests/snippets/scheme/strings.txt
new file mode 100644
index 0000000..9d03351
--- /dev/null
+++ b/tests/snippets/scheme/strings.txt
@@ -0,0 +1,85 @@
+---input---
+;; Test string escapes
+
+"basic string"
+
+"Strings can
+ span several
+
+ lines.
+ "
+
+"Escapes:
+ \"
+ \\
+ \|
+ \a
+ \f
+ \n
+ \r
+ \t
+ \v
+ \b
+ \0
+ \(
+ \
+ \x125612; (R6RS-style)
+ \x13 (Guile-style)
+ \u1234
+ \U123456
+"
+
+---tokens---
+';; Test string escapes' Comment.Single
+'\n\n' Text.Whitespace
+
+'"' Literal.String
+'basic string' Literal.String
+'"' Literal.String
+'\n\n' Text.Whitespace
+
+'"' Literal.String
+'Strings can\n span several\n\n lines.\n ' Literal.String
+'"' Literal.String
+'\n\n' Text.Whitespace
+
+'"' Literal.String
+'Escapes:\n ' Literal.String
+'\\"' Literal.String.Escape
+'\n ' Literal.String
+'\\\\' Literal.String.Escape
+'\n ' Literal.String
+'\\|' Literal.String.Escape
+'\n ' Literal.String
+'\\a' Literal.String.Escape
+'\n ' Literal.String
+'\\f' Literal.String.Escape
+'\n ' Literal.String
+'\\n' Literal.String.Escape
+'\n ' Literal.String
+'\\r' Literal.String.Escape
+'\n ' Literal.String
+'\\t' Literal.String.Escape
+'\n ' Literal.String
+'\\v' Literal.String.Escape
+'\n ' Literal.String
+'\\b' Literal.String.Escape
+'\n ' Literal.String
+'\\0' Literal.String.Escape
+'\n ' Literal.String
+'\\(' Literal.String.Escape
+'\n ' Literal.String
+'\\\n' Literal.String.Escape
+
+' ' Literal.String
+'\\x125612;' Literal.String.Escape
+' (R6RS-style)\n ' Literal.String
+'\\x13' Literal.String.Escape
+' (Guile-style)\n ' Literal.String
+'\\u1234' Literal.String.Escape
+'\n ' Literal.String
+'\\U123456' Literal.String.Escape
+'\n' Literal.String
+
+'"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/shell/test_array_nums.txt b/tests/snippets/shell/test_array_nums.txt
new file mode 100644
index 0000000..4e9870c
--- /dev/null
+++ b/tests/snippets/shell/test_array_nums.txt
@@ -0,0 +1,14 @@
+---input---
+a=(1 2 3)
+
+---tokens---
+'a' Name.Variable
+'=' Operator
+'(' Operator
+'1' Literal.Number
+' ' Text.Whitespace
+'2' Literal.Number
+' ' Text.Whitespace
+'3' Literal.Number
+')' Operator
+'\n' Text.Whitespace
diff --git a/tests/snippets/shell/test_curly_no_escape_and_quotes.txt b/tests/snippets/shell/test_curly_no_escape_and_quotes.txt
new file mode 100644
index 0000000..9fbb718
--- /dev/null
+++ b/tests/snippets/shell/test_curly_no_escape_and_quotes.txt
@@ -0,0 +1,15 @@
+---input---
+echo "${a//["b"]/}"
+
+---tokens---
+'echo' Name.Builtin
+' ' Text.Whitespace
+'"' Literal.String.Double
+'${' Literal.String.Interpol
+'a' Name.Variable
+'//[' Punctuation
+'"b"' Literal.String.Double
+']/' Punctuation
+'}' Literal.String.Interpol
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/shell/test_curly_with_escape.txt b/tests/snippets/shell/test_curly_with_escape.txt
new file mode 100644
index 0000000..d03b23e
--- /dev/null
+++ b/tests/snippets/shell/test_curly_with_escape.txt
@@ -0,0 +1,13 @@
+---input---
+echo ${a//[\"]/}
+
+---tokens---
+'echo' Name.Builtin
+' ' Text.Whitespace
+'${' Literal.String.Interpol
+'a' Name.Variable
+'//[' Punctuation
+'\\"' Literal.String.Escape
+']/' Punctuation
+'}' Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/shell/test_end_of_line_nums.txt b/tests/snippets/shell/test_end_of_line_nums.txt
new file mode 100644
index 0000000..663ea61
--- /dev/null
+++ b/tests/snippets/shell/test_end_of_line_nums.txt
@@ -0,0 +1,15 @@
+---input---
+a=1
+b=2 # comment
+
+---tokens---
+'a' Name.Variable
+'=' Operator
+'1' Literal.Number
+'\n' Text.Whitespace
+
+'b' Name.Variable
+'=' Operator
+'2' Literal.Number
+' ' Text.Whitespace
+'# comment\n' Comment.Single
diff --git a/tests/snippets/shell/test_parsed_single.txt b/tests/snippets/shell/test_parsed_single.txt
new file mode 100644
index 0000000..3110c8e
--- /dev/null
+++ b/tests/snippets/shell/test_parsed_single.txt
@@ -0,0 +1,8 @@
+---input---
+a=$'abc\''
+
+---tokens---
+'a' Name.Variable
+'=' Operator
+"$'abc\\''" Literal.String.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/shell/test_short_variable_names.txt b/tests/snippets/shell/test_short_variable_names.txt
new file mode 100644
index 0000000..86d4dc1
--- /dev/null
+++ b/tests/snippets/shell/test_short_variable_names.txt
@@ -0,0 +1,26 @@
+---input---
+x="$"
+y="$_"
+z="$abc"
+
+---tokens---
+'x' Name.Variable
+'=' Operator
+'"' Literal.String.Double
+'$' Text
+'"' Literal.String.Double
+'\n' Text.Whitespace
+
+'y' Name.Variable
+'=' Operator
+'"' Literal.String.Double
+'$_' Name.Variable
+'"' Literal.String.Double
+'\n' Text.Whitespace
+
+'z' Name.Variable
+'=' Operator
+'"' Literal.String.Double
+'$abc' Name.Variable
+'"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/shexc/test_prefixed_name_starting_with_number.txt b/tests/snippets/shexc/test_prefixed_name_starting_with_number.txt
new file mode 100644
index 0000000..ca1c293
--- /dev/null
+++ b/tests/snippets/shexc/test_prefixed_name_starting_with_number.txt
@@ -0,0 +1,8 @@
+---input---
+alice:6f6e4241-75a2-4780-9b2a-40da53082e54
+
+---tokens---
+'alice' Name.Namespace
+':' Punctuation
+'6f6e4241-75a2-4780-9b2a-40da53082e54' Name.Tag
+'\n' Text
diff --git a/tests/snippets/smarty/test_nested_curly.txt b/tests/snippets/smarty/test_nested_curly.txt
new file mode 100644
index 0000000..493aa49
--- /dev/null
+++ b/tests/snippets/smarty/test_nested_curly.txt
@@ -0,0 +1,18 @@
+---input---
+{templateFunction param={anotherFunction} param2=$something}
+
+---tokens---
+'{' Comment.Preproc
+'templateFunction' Name.Function
+' ' Text
+'param' Name.Attribute
+'=' Operator
+'{' Comment.Preproc
+'anotherFunction' Name.Attribute
+'}' Comment.Preproc
+' ' Text
+'param2' Name.Attribute
+'=' Operator
+'$something' Name.Variable
+'}' Comment.Preproc
+'\n' Other
diff --git a/tests/snippets/snbt/json.txt b/tests/snippets/snbt/json.txt
new file mode 100644
index 0000000..7b02134
--- /dev/null
+++ b/tests/snippets/snbt/json.txt
@@ -0,0 +1,43 @@
+---input---
+{
+ "text": "This is JSON",
+ "extra": [
+ "however",
+ "it's also technically valid SNBT.."
+ ]
+}
+
+---tokens---
+'{' Punctuation
+'\n ' Text.Whitespace
+'"' Literal.String.Double
+'text' Literal.String.Double
+'"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'This is JSON' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'"' Literal.String.Double
+'extra' Literal.String.Double
+'"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'\n ' Text.Whitespace
+'"' Literal.String.Double
+'however' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'"' Literal.String.Double
+"it's also technically valid SNBT.." Literal.String.Double
+'"' Literal.String.Double
+'\n ' Text.Whitespace
+']' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text
diff --git a/tests/snippets/snbt/literals.txt b/tests/snippets/snbt/literals.txt
new file mode 100644
index 0000000..cc852b8
--- /dev/null
+++ b/tests/snippets/snbt/literals.txt
@@ -0,0 +1,41 @@
+---input---
+{int: 1, byte: 0b, short: 1s, long: 10000L, float: 10.0f, double: 20.0}
+
+---tokens---
+'{' Punctuation
+'int' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'1' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'byte' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'0b' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'short' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'1s' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'long' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'10000L' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'float' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'10.0f' Literal.Number.Float
+',' Punctuation
+' ' Text.Whitespace
+'double' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'20.0' Literal.Number.Float
+'}' Punctuation
+'\n' Text
diff --git a/tests/snippets/snbt/multiline.txt b/tests/snippets/snbt/multiline.txt
new file mode 100644
index 0000000..35a6a53
--- /dev/null
+++ b/tests/snippets/snbt/multiline.txt
@@ -0,0 +1,56 @@
+---input---
+{
+ key: "cool compound",
+ UUID: [I;459130179,987513928,-1425663264,-175461800],
+ list: [
+ 0,
+ 1,
+ 3
+ ]
+}
+
+---tokens---
+'{' Punctuation
+'\n ' Text.Whitespace
+'key' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'cool compound' Literal.String.Double
+'"' Literal.String.Double
+',' Punctuation
+'\n ' Text.Whitespace
+'UUID' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'I' Name.Attribute
+';' Punctuation
+'459130179' Literal.Number.Integer
+',' Punctuation
+'987513928' Literal.Number.Integer
+',' Punctuation
+'-1425663264' Literal.Number.Integer
+',' Punctuation
+'-175461800' Literal.Number.Integer
+']' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'list' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'\n ' Text.Whitespace
+'0' Literal.Number.Integer
+',' Punctuation
+'\n ' Text.Whitespace
+'1' Literal.Number.Integer
+',' Punctuation
+'\n ' Text.Whitespace
+'3' Literal.Number.Integer
+'\n ' Text.Whitespace
+']' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text
diff --git a/tests/snippets/snbt/nesting.txt b/tests/snippets/snbt/nesting.txt
new file mode 100644
index 0000000..317a9c8
--- /dev/null
+++ b/tests/snippets/snbt/nesting.txt
@@ -0,0 +1,39 @@
+---input---
+{root: [{compound: 1b}, {compound: 2b, tag: {key: "value"}}]}
+
+---tokens---
+'{' Punctuation
+'root' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'{' Punctuation
+'compound' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'1b' Literal.Number.Integer
+'}' Punctuation
+',' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'compound' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'2b' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'tag' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'key' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'value' Literal.String.Double
+'"' Literal.String.Double
+'}' Punctuation
+'}' Punctuation
+']' Punctuation
+'}' Punctuation
+'\n' Text
diff --git a/tests/snippets/snbt/quoted_keys.txt b/tests/snippets/snbt/quoted_keys.txt
new file mode 100644
index 0000000..8e54f5f
--- /dev/null
+++ b/tests/snippets/snbt/quoted_keys.txt
@@ -0,0 +1,29 @@
+---input---
+{"special--key": 10b, normal_key: false, "json..///_type_key": "yup"}
+
+---tokens---
+'{' Punctuation
+'"' Literal.String.Double
+'special--key' Literal.String.Double
+'"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'10b' Literal.Number.Integer
+',' Punctuation
+' ' Text.Whitespace
+'normal_key' Name.Attribute
+':' Punctuation
+' ' Text.Whitespace
+'false' Name.Attribute
+',' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'json..///_type_key' Literal.String.Double
+'"' Literal.String.Double
+':' Punctuation
+' ' Text.Whitespace
+'"' Literal.String.Double
+'yup' Literal.String.Double
+'"' Literal.String.Double
+'}' Punctuation
+'\n' Text
diff --git a/tests/snippets/systemverilog/test_basic.txt b/tests/snippets/systemverilog/test_basic.txt
new file mode 100644
index 0000000..1f86ed0
--- /dev/null
+++ b/tests/snippets/systemverilog/test_basic.txt
@@ -0,0 +1,157 @@
+# Examine tokens emitted by the SV lexer for a trivial module.
+# Not intended to stress any particular corner of the language.
+
+---input---
+// Adder flops the sum of its inputs
+module Adder #(
+ parameter int N = 42
+) (
+ output logic [N-1:0] y,
+ output logic co,
+
+ input logic [N-1:0] a,
+ input logic [N-1:0] b,
+ input logic ci,
+
+ input logic clk
+);
+ always_ff @(posedge clk) begin
+ {co, y} <= a + b + ci;
+ end
+endmodule : Adder
+
+---tokens---
+'// Adder flops the sum of its inputs\n' Comment.Single
+
+'module' Keyword
+' ' Text.Whitespace
+'Adder' Name
+' ' Text.Whitespace
+'#' Punctuation
+'(' Punctuation
+'\n ' Text.Whitespace
+'parameter' Keyword
+' ' Text.Whitespace
+'int' Keyword.Type
+' ' Text.Whitespace
+'N' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'42' Literal.Number.Integer
+'\n' Text.Whitespace
+
+')' Punctuation
+' ' Text.Whitespace
+'(' Punctuation
+'\n ' Text.Whitespace
+'output' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'[' Punctuation
+'N' Name
+'-' Operator
+'1' Literal.Number.Integer
+':' Operator
+'0' Literal.Number.Integer
+']' Punctuation
+' ' Text.Whitespace
+'y' Name
+',' Punctuation
+'\n ' Text.Whitespace
+'output' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'co' Name
+',' Punctuation
+'\n\n ' Text.Whitespace
+'input' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'[' Punctuation
+'N' Name
+'-' Operator
+'1' Literal.Number.Integer
+':' Operator
+'0' Literal.Number.Integer
+']' Punctuation
+' ' Text.Whitespace
+'a' Name
+',' Punctuation
+'\n ' Text.Whitespace
+'input' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'[' Punctuation
+'N' Name
+'-' Operator
+'1' Literal.Number.Integer
+':' Operator
+'0' Literal.Number.Integer
+']' Punctuation
+' ' Text.Whitespace
+'b' Name
+',' Punctuation
+'\n ' Text.Whitespace
+'input' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'ci' Name
+',' Punctuation
+'\n\n ' Text.Whitespace
+'input' Keyword
+' ' Text.Whitespace
+'logic' Keyword.Type
+' ' Text.Whitespace
+'clk' Name
+'\n' Text.Whitespace
+
+')' Punctuation
+';' Punctuation
+'\n ' Text.Whitespace
+'always_ff' Keyword
+' ' Text.Whitespace
+'@' Punctuation
+'(' Punctuation
+'posedge' Keyword
+' ' Text.Whitespace
+'clk' Name
+')' Punctuation
+' ' Text.Whitespace
+'begin' Keyword
+'\n ' Text.Whitespace
+'{' Punctuation
+'co' Name
+',' Punctuation
+' ' Text.Whitespace
+'y' Name
+'}' Punctuation
+' ' Text.Whitespace
+'<' Operator
+'=' Operator
+' ' Text.Whitespace
+'a' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'b' Name
+' ' Text.Whitespace
+'+' Operator
+' ' Text.Whitespace
+'ci' Name
+';' Punctuation
+'\n ' Text.Whitespace
+'end' Keyword
+'\n' Text.Whitespace
+
+'endmodule' Keyword
+' ' Text.Whitespace
+':' Operator
+' ' Text.Whitespace
+'Adder' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/systemverilog/test_classes.txt b/tests/snippets/systemverilog/test_classes.txt
new file mode 100644
index 0000000..b9529db
--- /dev/null
+++ b/tests/snippets/systemverilog/test_classes.txt
@@ -0,0 +1,89 @@
+# Most of the interesting types of class declarations
+
+---input---
+class Foo;
+endclass
+
+class Bar;
+endclass : Bar
+
+class Fiz extends Buz;
+endclass : Fiz
+
+class Free #(parameter type T = byte) extends Beer #(T);
+endclass : Free
+
+---tokens---
+'class' Keyword.Declaration
+' ' Text.Whitespace
+'Foo' Name.Class
+';' Punctuation
+'\n' Text.Whitespace
+
+'endclass' Keyword.Declaration
+'\n\n' Text.Whitespace
+
+'class' Keyword.Declaration
+' ' Text.Whitespace
+'Bar' Name.Class
+';' Punctuation
+'\n' Text.Whitespace
+
+'endclass' Keyword.Declaration
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'Bar' Name.Class
+'\n\n' Text.Whitespace
+
+'class' Keyword.Declaration
+' ' Text.Whitespace
+'Fiz' Name.Class
+' ' Text.Whitespace
+'extends' Keyword.Declaration
+' ' Text.Whitespace
+'Buz' Name.Class
+';' Punctuation
+'\n' Text.Whitespace
+
+'endclass' Keyword.Declaration
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'Fiz' Name.Class
+'\n\n' Text.Whitespace
+
+'class' Keyword.Declaration
+' ' Text.Whitespace
+'Free' Name.Class
+' ' Text.Whitespace
+'#' Punctuation
+'(' Punctuation
+'parameter' Keyword
+' ' Text.Whitespace
+'type' Keyword.Type
+' ' Text.Whitespace
+'T' Name
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'byte' Keyword.Type
+')' Punctuation
+' ' Text.Whitespace
+'extends' Keyword.Declaration
+' ' Text.Whitespace
+'Beer' Name.Class
+' ' Text.Whitespace
+'#' Punctuation
+'(' Punctuation
+'T' Name
+')' Punctuation
+';' Punctuation
+'\n' Text.Whitespace
+
+'endclass' Keyword.Declaration
+' ' Text.Whitespace
+':' Punctuation
+' ' Text.Whitespace
+'Free' Name.Class
+'\n' Text.Whitespace
diff --git a/tests/snippets/systemverilog/test_numbers.txt b/tests/snippets/systemverilog/test_numbers.txt
new file mode 100644
index 0000000..d7e4511
--- /dev/null
+++ b/tests/snippets/systemverilog/test_numbers.txt
@@ -0,0 +1,158 @@
+# Believe it or not, SystemVerilog supports spaces before and after the base
+# specifier (ie 'b, 'd, 'h). See IEEE 1800-2017 Section 5.7.1 for examples.
+
+---input---
+8'b10101010
+8 'b10101010
+8'b 10101010
+8'sb10101010
+8'Sb10101010
+8'B10101010
+8'b1010_1010
+8'b10xXzZ?10
+
+24'o01234567
+24 'o01234567
+24'o 01234567
+24'so01234567
+24'So01234567
+24'O01234567
+24'o0123_4567
+24'o01xXzZ?7
+
+32'd27182818
+32 'd27182818
+32'd 27182818
+32'sd27182818
+32'Sd27182818
+32'D27182818
+32'd2718_2818
+32'd27xXzZ?8
+
+32'hdeadbeef
+32 'hdeadbeef
+32'h deadbeef
+32'shdeadbeef
+32'Shdeadbeef
+32'Hdeadbeef
+32'hdead_beef
+32'hdexXzZ?f
+
+'0 '1 'x 'X 'z 'Z
+
+42 1234_5678
+
+---tokens---
+"8'b10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8 'b10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'b 10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'sb10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'Sb10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'B10101010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'b1010_1010" Literal.Number.Bin
+'\n' Text.Whitespace
+
+"8'b10xXzZ?10" Literal.Number.Bin
+'\n\n' Text.Whitespace
+
+"24'o01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24 'o01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'o 01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'so01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'So01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'O01234567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'o0123_4567" Literal.Number.Oct
+'\n' Text.Whitespace
+
+"24'o01xXzZ?7" Literal.Number.Oct
+'\n\n' Text.Whitespace
+
+"32'd27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32 'd27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'd 27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'sd27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'Sd27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'D27182818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'd2718_2818" Literal.Number.Integer
+'\n' Text.Whitespace
+
+"32'd27xXzZ?8" Literal.Number.Integer
+'\n\n' Text.Whitespace
+
+"32'hdeadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32 'hdeadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'h deadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'shdeadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'Shdeadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'Hdeadbeef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'hdead_beef" Literal.Number.Hex
+'\n' Text.Whitespace
+
+"32'hdexXzZ?f" Literal.Number.Hex
+'\n\n' Text.Whitespace
+
+"'0" Literal.Number
+' ' Text.Whitespace
+"'1" Literal.Number
+' ' Text.Whitespace
+"'x" Literal.Number
+' ' Text.Whitespace
+"'X" Literal.Number
+' ' Text.Whitespace
+"'z" Literal.Number
+' ' Text.Whitespace
+"'Z" Literal.Number
+'\n\n' Text.Whitespace
+
+'42' Literal.Number.Integer
+' ' Text.Whitespace
+'1234_5678' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/systemverilog/test_operators.txt b/tests/snippets/systemverilog/test_operators.txt
new file mode 100644
index 0000000..fec4539
--- /dev/null
+++ b/tests/snippets/systemverilog/test_operators.txt
@@ -0,0 +1,213 @@
+# See 1800-2017 Table 11-2: Operator Precedence and Associativity
+# Note that the duplicates (unary/binary) have been removed,
+# ie '+', '-', '&', '|', '^', '~^', '^~'
+# Note: This is a inconsistent mix of operator and punctuation
+# Note: Operators would ideally be represented as one token: ':' ':' -> '::', '~' '&' -> '~&'
+
+---input---
+() [] :: .
++ - ! ~ & ~& | ~| ^ ~^ ^~ ++ --
+**
+* / %
+<< >> <<< >>>
+< <= > >= inside dist
+== != === !== ==? !=?
+&&
+||
+?:
+-> <->
+= += -= *= /= %= &= ^= |= <<= >>= <<<= >>>= := :/ <=
+{} {{}}
+
+---tokens---
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+']' Punctuation
+' ' Text.Whitespace
+':' Operator
+':' Operator
+' ' Text.Whitespace
+'.' Punctuation
+'\n' Text.Whitespace
+
+'+' Operator
+' ' Text.Whitespace
+'-' Operator
+' ' Text.Whitespace
+'!' Operator
+' ' Text.Whitespace
+'~' Operator
+' ' Text.Whitespace
+'&' Operator
+' ' Text.Whitespace
+'~' Operator
+'&' Operator
+' ' Text.Whitespace
+'|' Operator
+' ' Text.Whitespace
+'~' Operator
+'|' Operator
+' ' Text.Whitespace
+'^' Operator
+' ' Text.Whitespace
+'~' Operator
+'^' Operator
+' ' Text.Whitespace
+'^' Operator
+'~' Operator
+' ' Text.Whitespace
+'+' Operator
+'+' Operator
+' ' Text.Whitespace
+'-' Operator
+'-' Operator
+'\n' Text.Whitespace
+
+'*' Operator
+'*' Operator
+'\n' Text.Whitespace
+
+'*' Operator
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'%' Operator
+'\n' Text.Whitespace
+
+'<' Operator
+'<' Operator
+' ' Text.Whitespace
+'>' Operator
+'>' Operator
+' ' Text.Whitespace
+'<' Operator
+'<' Operator
+'<' Operator
+' ' Text.Whitespace
+'>' Operator
+'>' Operator
+'>' Operator
+'\n' Text.Whitespace
+
+'<' Operator
+' ' Text.Whitespace
+'<' Operator
+'=' Operator
+' ' Text.Whitespace
+'>' Operator
+' ' Text.Whitespace
+'>' Operator
+'=' Operator
+' ' Text.Whitespace
+'inside' Operator.Word
+' ' Text.Whitespace
+'dist' Operator.Word
+'\n' Text.Whitespace
+
+'=' Operator
+'=' Operator
+' ' Text.Whitespace
+'!' Operator
+'=' Operator
+' ' Text.Whitespace
+'=' Operator
+'=' Operator
+'=' Operator
+' ' Text.Whitespace
+'!' Operator
+'=' Operator
+'=' Operator
+' ' Text.Whitespace
+'=' Operator
+'=' Operator
+'?' Operator
+' ' Text.Whitespace
+'!' Operator
+'=' Operator
+'?' Operator
+'\n' Text.Whitespace
+
+'&' Operator
+'&' Operator
+'\n' Text.Whitespace
+
+'|' Operator
+'|' Operator
+'\n' Text.Whitespace
+
+'?' Operator
+':' Operator
+'\n' Text.Whitespace
+
+'-' Operator
+'>' Operator
+' ' Text.Whitespace
+'<' Operator
+'-' Operator
+'>' Operator
+'\n' Text.Whitespace
+
+'=' Operator
+' ' Text.Whitespace
+'+' Operator
+'=' Operator
+' ' Text.Whitespace
+'-' Operator
+'=' Operator
+' ' Text.Whitespace
+'*' Operator
+'=' Operator
+' ' Text.Whitespace
+'/' Operator
+'=' Operator
+' ' Text.Whitespace
+'%' Operator
+'=' Operator
+' ' Text.Whitespace
+'&' Operator
+'=' Operator
+' ' Text.Whitespace
+'^' Operator
+'=' Operator
+' ' Text.Whitespace
+'|' Operator
+'=' Operator
+' ' Text.Whitespace
+'<' Operator
+'<' Operator
+'=' Operator
+' ' Text.Whitespace
+'>' Operator
+'>' Operator
+'=' Operator
+' ' Text.Whitespace
+'<' Operator
+'<' Operator
+'<' Operator
+'=' Operator
+' ' Text.Whitespace
+'>' Operator
+'>' Operator
+'>' Operator
+'=' Operator
+' ' Text.Whitespace
+':' Operator
+'=' Operator
+' ' Text.Whitespace
+':' Operator
+'/' Operator
+' ' Text.Whitespace
+'<' Operator
+'=' Operator
+'\n' Text.Whitespace
+
+'{' Punctuation
+'}' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'{' Punctuation
+'}' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/tcl/test_comma_and_at.txt b/tests/snippets/tcl/test_comma_and_at.txt
new file mode 100644
index 0000000..51e1733
--- /dev/null
+++ b/tests/snippets/tcl/test_comma_and_at.txt
@@ -0,0 +1,131 @@
+---input---
+# Alt and arrow keys to scroll
+set scroll_amount 2
+bind Text <Alt-Up> {
+ %W yview scroll -$scroll_amount units
+ %W mark set insert @0,[expr [winfo height %W] / 2]
+}
+bind Text <Alt-Down> {
+ %W yview scroll $scroll_amount units
+ %W mark set insert @0,[expr [winfo height %W] / 2]
+}
+
+---tokens---
+'#' Comment
+' Alt and arrow keys to scroll\n' Comment
+
+'set' Keyword
+' ' Text.Whitespace
+'scroll_amount' Text
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+'\n' Text
+
+'bind' Name.Variable
+' ' Text.Whitespace
+'Text' Text
+' ' Text.Whitespace
+'<' Operator
+'Alt-Up' Text
+'>' Operator
+' ' Text.Whitespace
+'{' Keyword
+'\n ' Text.Whitespace
+'%' Operator
+'W' Name.Variable
+' ' Text.Whitespace
+'yview' Text
+' ' Text.Whitespace
+'scroll' Text
+' ' Text.Whitespace
+'-' Operator
+'$scroll_amount' Name.Variable
+' ' Text.Whitespace
+'units' Text
+'\n' Text
+
+' ' Text.Whitespace
+'%' Operator
+'W' Name.Variable
+' ' Text.Whitespace
+'mark' Text
+' ' Text.Whitespace
+'set' Text
+' ' Text.Whitespace
+'insert' Text
+' ' Text.Whitespace
+'@0,' Text
+'[' Keyword
+'expr' Keyword
+' ' Text.Whitespace
+'[' Keyword
+'winfo' Name.Variable
+' ' Text.Whitespace
+'height' Text
+' ' Text.Whitespace
+'%' Operator
+'W' Text
+']' Keyword
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+']' Keyword
+'\n' Text
+
+'}' Keyword
+'\n' Text
+
+'bind' Name.Variable
+' ' Text.Whitespace
+'Text' Text
+' ' Text.Whitespace
+'<' Operator
+'Alt-Down' Text
+'>' Operator
+' ' Text.Whitespace
+'{' Keyword
+'\n ' Text.Whitespace
+'%' Operator
+'W' Name.Variable
+' ' Text.Whitespace
+'yview' Text
+' ' Text.Whitespace
+'scroll' Text
+' ' Text.Whitespace
+'$scroll_amount' Name.Variable
+' ' Text.Whitespace
+'units' Text
+'\n' Text
+
+' ' Text.Whitespace
+'%' Operator
+'W' Name.Variable
+' ' Text.Whitespace
+'mark' Text
+' ' Text.Whitespace
+'set' Text
+' ' Text.Whitespace
+'insert' Text
+' ' Text.Whitespace
+'@0,' Text
+'[' Keyword
+'expr' Keyword
+' ' Text.Whitespace
+'[' Keyword
+'winfo' Name.Variable
+' ' Text.Whitespace
+'height' Text
+' ' Text.Whitespace
+'%' Operator
+'W' Text
+']' Keyword
+' ' Text.Whitespace
+'/' Operator
+' ' Text.Whitespace
+'2' Literal.Number.Integer
+']' Keyword
+'\n' Text
+
+'}' Keyword
+'\n' Text
diff --git a/tests/snippets/tcl/test_vars.txt b/tests/snippets/tcl/test_vars.txt
new file mode 100644
index 0000000..5c62390
--- /dev/null
+++ b/tests/snippets/tcl/test_vars.txt
@@ -0,0 +1,17 @@
+---input---
+set size 10; puts ${size}x${size}
+
+---tokens---
+'set' Keyword
+' ' Text.Whitespace
+'size' Text
+' ' Text.Whitespace
+'10' Literal.Number.Integer
+';' Keyword
+' ' Text.Whitespace
+'puts' Name.Builtin
+' ' Text.Whitespace
+'${size}' Name.Variable
+'x' Text
+'${size}' Name.Variable
+'\n' Text
diff --git a/tests/snippets/teal/test_comments.txt b/tests/snippets/teal/test_comments.txt
new file mode 100644
index 0000000..df7e1b1
--- /dev/null
+++ b/tests/snippets/teal/test_comments.txt
@@ -0,0 +1,28 @@
+---input---
+a//c1
+ //c2
+label://c3
+a // c4
+label: implicit comment
+
+---tokens---
+'a' Name.Function
+'//c1' Comment.Single
+'\n' Text
+
+' ' Text.Whitespace
+'//c2' Comment.Single
+'\n' Text.Whitespace
+
+'label:' Name.Function
+'//c3' Comment.Single
+'\n' Text
+
+'a' Name.Function
+' ' Text.Whitespace
+'// c4' Comment.Single
+'\n' Text
+
+'label:' Name.Label
+' implicit comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/teal/test_literals.txt b/tests/snippets/teal/test_literals.txt
new file mode 100644
index 0000000..46e43dc
--- /dev/null
+++ b/tests/snippets/teal/test_literals.txt
@@ -0,0 +1,28 @@
+---input---
+a 0x1AaAF
+a 7777777777777777777777777777777777777777777777777774MSJUVU
+a base32(aB/c23=)
+a b64 aB/c23=
+
+---tokens---
+'a' Name.Function
+' ' Text.Whitespace
+'0x1AaAF' Literal.Number.Hex
+'\n' Text
+
+'a' Name.Function
+' ' Text.Whitespace
+'7777777777777777777777777777777777777777777777777774MSJUVU' Literal.Number
+'\n' Text
+
+'a' Name.Function
+' ' Text.Whitespace
+'base32' Literal.String.Affix
+'(aB/c23=)' Literal.String.Other
+'\n' Text
+
+'a' Name.Function
+' ' Text.Whitespace
+'b64 ' Literal.String.Affix
+'aB/c23=' Literal.String.Other
+'\n' Text
diff --git a/tests/snippets/teal/test_strings.txt b/tests/snippets/teal/test_strings.txt
new file mode 100644
index 0000000..4e32633
--- /dev/null
+++ b/tests/snippets/teal/test_strings.txt
@@ -0,0 +1,15 @@
+---input---
+a "abc\x123\n\"de//f"
+
+---tokens---
+'a' Name.Function
+' ' Text.Whitespace
+'"' Literal.String
+'abc' Literal.String
+'\\x12' Literal.String.Escape
+'3' Literal.String
+'\\n' Literal.String.Escape
+'\\"' Literal.String.Escape
+'de//f' Literal.String
+'"' Literal.String
+'\n' Text
diff --git a/tests/snippets/terraform/test_attributes.txt b/tests/snippets/terraform/test_attributes.txt
new file mode 100644
index 0000000..2fcf805
--- /dev/null
+++ b/tests/snippets/terraform/test_attributes.txt
@@ -0,0 +1,155 @@
+---input---
+ description = "Some description"
+
+ availability_zones = ["${aws_instance.web.availability_zone}-foobar"]
+ availability_zones = [aws_instance.web.availability_zone]
+ assume_role_policy = data.aws_iam_policy_document.trust.json
+ policy_arn = aws_iam_policy.assume_roles[0].arn
+
+ value = file("path.txt")
+ value = jsonencode(element("value"))
+
+ tags = {
+ Name = "something"
+ }
+
+ "ENV_VARIABLE_1" = aws_dynamodb_table.loginsights2metrics.name
+ "ENV_VARIABLE_2" = "Some string"
+
+ ignore_changes = [last_modified, filename]
+
+ variable = "aws:MultiFactorAuthPresent"
+
+---tokens---
+' ' Text.Whitespace
+'description' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"Some description"' Literal.String.Double
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'availability_zones' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'"${aws_instance.web.availability_zone}-foobar"' Literal.String.Double
+']' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'availability_zones' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'aws_instance.web.availability_zone' Name.Variable
+']' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'assume_role_policy' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'data.aws_iam_policy_document.trust.json' Name.Variable
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'policy_arn' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'aws_iam_policy.assume_roles[0].arn' Name.Variable
+'\n' Text.Whitespace
+
+' \n ' Text.Whitespace
+'value' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'file' Name.Function
+'(' Punctuation
+'"path.txt"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'value' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'jsonencode' Name.Function
+'(' Punctuation
+'element' Name.Function
+'(' Punctuation
+'"value"' Literal.String.Double
+')' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'tags' Name.Builtin
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'Name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"something"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"ENV_VARIABLE_1"' Literal.String.Double
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'aws_dynamodb_table.loginsights2metrics.name' Name.Variable
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'"ENV_VARIABLE_2"' Literal.String.Double
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"Some string"' Literal.String.Double
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'ignore_changes' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'last_modified, filename' Name.Builtin
+']' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'variable' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"aws:MultiFactorAuthPresent"' Literal.String.Double
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_backend.txt b/tests/snippets/terraform/test_backend.txt
new file mode 100644
index 0000000..ff02a58
--- /dev/null
+++ b/tests/snippets/terraform/test_backend.txt
@@ -0,0 +1,44 @@
+---input---
+terraform {
+ backend "consul" {
+ address = "demo.consul.io"
+ path = "tfdocs"
+ }
+}
+
+---tokens---
+'terraform' Name.Builtin
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'backend' Keyword.Reserved
+' ' Text.Whitespace
+'"consul"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'address' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"demo.consul.io"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'path' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"tfdocs"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_comment.txt b/tests/snippets/terraform/test_comment.txt
new file mode 100644
index 0000000..9f5c1a7
--- /dev/null
+++ b/tests/snippets/terraform/test_comment.txt
@@ -0,0 +1,64 @@
+---input---
+# Single line comment
+// Non-idiomatic single line comment
+/* multiline
+
+ comment
+
+*/
+provider "azurerm" { # (1)
+ features {}
+}
+
+---tokens---
+'# Single line comment\n' Comment.Single
+
+'// Non-idiomatic single line comment\n' Comment.Single
+
+'/*' Comment.Multiline
+' ' Comment.Multiline
+'m' Comment.Multiline
+'u' Comment.Multiline
+'l' Comment.Multiline
+'t' Comment.Multiline
+'i' Comment.Multiline
+'l' Comment.Multiline
+'i' Comment.Multiline
+'n' Comment.Multiline
+'e' Comment.Multiline
+'\n' Comment.Multiline
+
+'\n' Comment.Multiline
+
+' ' Comment.Multiline
+' ' Comment.Multiline
+'c' Comment.Multiline
+'o' Comment.Multiline
+'m' Comment.Multiline
+'m' Comment.Multiline
+'e' Comment.Multiline
+'n' Comment.Multiline
+'t' Comment.Multiline
+'\n' Comment.Multiline
+
+'\n' Comment.Multiline
+
+'*/' Comment.Multiline
+'\n' Text.Whitespace
+
+'provider' Keyword.Reserved
+' ' Text.Whitespace
+'"azurerm"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+' # (1)\n' Comment.Single
+
+' ' Text.Whitespace
+'features' Name.Builtin
+' ' Text.Whitespace
+'{' Punctuation
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+' \n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_functions.txt b/tests/snippets/terraform/test_functions.txt
new file mode 100644
index 0000000..b00c7a5
--- /dev/null
+++ b/tests/snippets/terraform/test_functions.txt
@@ -0,0 +1,56 @@
+---input---
+provider "aws" {
+ value = file("path.txt")
+}
+
+provider "aws" {
+ value = jsonencode(element("value"))
+}
+
+---tokens---
+'provider' Keyword.Reserved
+' ' Text.Whitespace
+'"aws"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'value' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'file' Name.Function
+'(' Punctuation
+'"path.txt"' Literal.String.Double
+')' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'provider' Keyword.Reserved
+' ' Text.Whitespace
+'"aws"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'value' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'jsonencode' Name.Function
+'(' Punctuation
+'element' Name.Function
+'(' Punctuation
+'"value"' Literal.String.Double
+')' Punctuation
+')' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_heredoc.txt b/tests/snippets/terraform/test_heredoc.txt
new file mode 100644
index 0000000..a8b1cb3
--- /dev/null
+++ b/tests/snippets/terraform/test_heredoc.txt
@@ -0,0 +1,65 @@
+---input---
+resource "local_file" "heredoc" {
+ content = <<-DOC
+ heredoc content
+ DOC
+}
+
+resource "local_file" "heredoc" {
+ content = <<DOC
+ heredoc content
+ DOC
+}
+
+---tokens---
+'resource' Keyword.Reserved
+' ' Text.Whitespace
+'"local_file"' Name.Class
+' ' Text.Whitespace
+'"heredoc"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'content' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'<<-' Operator
+'DOC' Literal.String.Delimiter
+'\n' Literal.String.Heredoc
+
+' heredoc content\n' Literal.String.Heredoc
+
+' DOC\n' Literal.String.Delimiter
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'resource' Keyword.Reserved
+' ' Text.Whitespace
+'"local_file"' Name.Class
+' ' Text.Whitespace
+'"heredoc"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'content' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'<<' Operator
+'DOC' Literal.String.Delimiter
+'\n' Literal.String.Heredoc
+
+' heredoc content\n' Literal.String.Heredoc
+
+' DOC\n' Literal.String.Delimiter
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_module.txt b/tests/snippets/terraform/test_module.txt
new file mode 100644
index 0000000..5a5876b
--- /dev/null
+++ b/tests/snippets/terraform/test_module.txt
@@ -0,0 +1,32 @@
+---input---
+module "consul" {
+ source = "hashicorp/consul/aws"
+ servers = 3
+}
+
+---tokens---
+'module' Keyword.Reserved
+' ' Text.Whitespace
+'"consul"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'source' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"hashicorp/consul/aws"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'servers' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'3' Literal.Number
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_resource.txt b/tests/snippets/terraform/test_resource.txt
new file mode 100644
index 0000000..7b2815a
--- /dev/null
+++ b/tests/snippets/terraform/test_resource.txt
@@ -0,0 +1,211 @@
+---input---
+resource "aws_internet_gateway" "base_igw" {
+ vpc_id = aws_vpc.something.id
+ tags = {
+ Name = "igw-${var.something}-${var.something}"
+ }
+}
+
+resource "aws_security_group" "allow_tls" {
+ name = "allow_tls"
+ description = "Allow TLS inbound traffic"
+ vpc_id = aws_vpc.main.id
+
+ # Ingress rules
+ ingress {
+ description = "TLS from VPC"
+ from_port = 443
+ to_port = 443
+ }
+
+ # Egress rules
+ egress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ tags = {
+ Name = "allow_tls"
+ }
+}
+
+---tokens---
+'resource' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_internet_gateway"' Name.Class
+' ' Text.Whitespace
+'"base_igw"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'vpc_id' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'aws_vpc.something.id' Name.Variable
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'tags' Name.Builtin
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'Name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"igw-${var.something}-${var.something}"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'resource' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_security_group"' Name.Class
+' ' Text.Whitespace
+'"allow_tls"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"allow_tls"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'description' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"Allow TLS inbound traffic"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'vpc_id' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'aws_vpc.main.id' Name.Variable
+'\n\n # Ingress rules\n' Comment.Single
+
+' ' Text.Whitespace
+'ingress' Name.Builtin
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'description' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"TLS from VPC"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'from_port' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'443' Literal.Number
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'to_port' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'443' Literal.Number
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n\n # Egress rules\n' Comment.Single
+
+' ' Text.Whitespace
+'egress' Name.Builtin
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'from_port' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'0' Literal.Number
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'to_port' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'0' Literal.Number
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'protocol' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"-1"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'cidr_blocks' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'"0.0.0.0/0"' Literal.String.Double
+']' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'tags' Name.Builtin
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'Name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"allow_tls"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_types.txt b/tests/snippets/terraform/test_types.txt
new file mode 100644
index 0000000..b689244
--- /dev/null
+++ b/tests/snippets/terraform/test_types.txt
@@ -0,0 +1,94 @@
+---input---
+backend "consul" {
+data "aws_ami" "example" {
+module "consul" {
+output "instance_ip_addr" {
+provider "aws" {
+provisioner "local-exec" {
+resource "aws_internet_gateway" "base_igw" {
+variable "aws_region" {
+variable "set-str" {
+ type = set(string)
+}
+
+---tokens---
+'backend' Keyword.Reserved
+' ' Text.Whitespace
+'"consul"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'data' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_ami"' Name.Class
+' ' Text.Whitespace
+'"example"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'module' Keyword.Reserved
+' ' Text.Whitespace
+'"consul"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'output' Keyword.Reserved
+' ' Text.Whitespace
+'"instance_ip_addr"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'provider' Keyword.Reserved
+' ' Text.Whitespace
+'"aws"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'provisioner' Keyword.Reserved
+' ' Text.Whitespace
+'"local-exec"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'resource' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_internet_gateway"' Name.Class
+' ' Text.Whitespace
+'"base_igw"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'variable' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_region"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'variable' Keyword.Reserved
+' ' Text.Whitespace
+'"set-str"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'type' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'set' Keyword.Type
+'(' Punctuation
+'string' Keyword.Type
+')' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_variable_declaration.txt b/tests/snippets/terraform/test_variable_declaration.txt
new file mode 100644
index 0000000..72e515e
--- /dev/null
+++ b/tests/snippets/terraform/test_variable_declaration.txt
@@ -0,0 +1,41 @@
+---input---
+variable "aws_region" {
+ description = "AWS region to launch servers."
+ default = "us-west-2"
+ somevar = true
+}
+
+---tokens---
+'variable' Keyword.Reserved
+' ' Text.Whitespace
+'"aws_region"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'description' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"AWS region to launch servers."' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'default' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"us-west-2"' Literal.String.Double
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'somevar' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'true' Name.Constant
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/terraform/test_variable_read.txt b/tests/snippets/terraform/test_variable_read.txt
new file mode 100644
index 0000000..25afcf4
--- /dev/null
+++ b/tests/snippets/terraform/test_variable_read.txt
@@ -0,0 +1,23 @@
+---input---
+provider "aws" {
+ region = var.aws_region
+}
+
+---tokens---
+'provider' Keyword.Reserved
+' ' Text.Whitespace
+'"aws"' Name.Variable
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+' ' Text.Whitespace
+'region' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'var.aws_region' Name.Variable
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/turtle/test_prefixed_name_starting_with_number.txt b/tests/snippets/turtle/test_prefixed_name_starting_with_number.txt
new file mode 100644
index 0000000..ca1c293
--- /dev/null
+++ b/tests/snippets/turtle/test_prefixed_name_starting_with_number.txt
@@ -0,0 +1,8 @@
+---input---
+alice:6f6e4241-75a2-4780-9b2a-40da53082e54
+
+---tokens---
+'alice' Name.Namespace
+':' Punctuation
+'6f6e4241-75a2-4780-9b2a-40da53082e54' Name.Tag
+'\n' Text
diff --git a/tests/snippets/typescript/test_function_definition.txt b/tests/snippets/typescript/test_function_definition.txt
new file mode 100644
index 0000000..925c380
--- /dev/null
+++ b/tests/snippets/typescript/test_function_definition.txt
@@ -0,0 +1,18 @@
+---input---
+async function main() {
+}
+
+---tokens---
+'async' Keyword
+' ' Text.Whitespace
+'function' Keyword.Declaration
+' ' Text.Whitespace
+'main' Name.Other
+'(' Punctuation
+')' Punctuation
+' ' Text.Whitespace
+'{' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/unixconfig/etc_group.txt b/tests/snippets/unixconfig/etc_group.txt
new file mode 100644
index 0000000..3294ed5
--- /dev/null
+++ b/tests/snippets/unixconfig/etc_group.txt
@@ -0,0 +1,45 @@
+---input---
+root:x:0:
+sudo:x:1:syslog,user
+syslog:x:2:
+#adm:x:3:
+
+user:x:1000
+
+---tokens---
+'root' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'\n' Text.Whitespace
+
+'sudo' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'1' Literal.Number
+':' Punctuation
+'syslog,user' Text
+'\n' Text.Whitespace
+
+'syslog' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'2' Literal.Number
+':' Punctuation
+'\n' Text.Whitespace
+
+'#adm:x:3:' Comment
+'\n' Text.Whitespace
+
+'\n' Text.Whitespace
+
+'user' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'1000' Literal.Number
+'\n' Text.Whitespace
diff --git a/tests/snippets/unixconfig/etc_passwd.txt b/tests/snippets/unixconfig/etc_passwd.txt
new file mode 100644
index 0000000..540e41f
--- /dev/null
+++ b/tests/snippets/unixconfig/etc_passwd.txt
@@ -0,0 +1,86 @@
+---input---
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
+#irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
+gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
+nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
+
+---tokens---
+'root' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'root' Text
+':' Punctuation
+'/root' Literal.String
+':' Punctuation
+'/bin/bash' Literal.String
+'\n' Text.Whitespace
+
+'daemon' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'1' Literal.Number
+':' Punctuation
+'1' Literal.Number
+':' Punctuation
+'daemon' Text
+':' Punctuation
+'/usr/sbin' Literal.String
+':' Punctuation
+'/usr/sbin/nologin' Literal.String
+'\n' Text.Whitespace
+
+'#irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin' Comment
+'\n' Text.Whitespace
+
+'gnats' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'41' Literal.Number
+':' Punctuation
+'41' Literal.Number
+':' Punctuation
+'Gnats Bug-Reporting System (admin)' Text
+':' Punctuation
+'/var/lib/gnats' Literal.String
+':' Punctuation
+'/usr/sbin/nologin' Literal.String
+'\n' Text.Whitespace
+
+'nobody' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'65534' Literal.Number
+':' Punctuation
+'65534' Literal.Number
+':' Punctuation
+'nobody' Text
+':' Punctuation
+'/nonexistent' Literal.String
+':' Punctuation
+'/usr/sbin/nologin' Literal.String
+'\n' Text.Whitespace
+
+'systemd-network' Text
+':' Punctuation
+'x' Literal.String
+':' Punctuation
+'100' Literal.Number
+':' Punctuation
+'102' Literal.Number
+':' Punctuation
+'systemd Network Management,,,' Text
+':' Punctuation
+'/run/systemd' Literal.String
+':' Punctuation
+'/usr/sbin/nologin' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/unixconfig/etc_shadow.txt b/tests/snippets/unixconfig/etc_shadow.txt
new file mode 100644
index 0000000..6b1d92a
--- /dev/null
+++ b/tests/snippets/unixconfig/etc_shadow.txt
@@ -0,0 +1,74 @@
+---input---
+root:$6$L95fNbtS$IZ8affe7h2B.DF81HZ:17262:0:14600:14:::
+#nobody:*:18375:0:99999:7:::
+bin:*:17110:0:99999:7:::
+user:$6$KmghZnvbZs7f3SQ9$H6f0M61q5Cf8JLrS0kR3M97/o6GzD6FH3MbLs92CM/l9mHZ7FngBzRfa8D5NrWl.K8nM64affeWrY/L0U7nBt/:19097:0:99999:7:::
+linoadmin:!!:17289:0:99999:7:::
+
+---tokens---
+'root' Text
+':' Punctuation
+'$6$L95fNbtS$IZ8affe7h2B.DF81HZ' Literal.String
+':' Punctuation
+'17262' Literal.Number
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'14600' Literal.Number
+':' Punctuation
+'14' Literal.Number
+':' Punctuation
+':' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+'#nobody:*:18375:0:99999:7:::' Comment
+'\n' Text.Whitespace
+
+'bin' Text
+':' Punctuation
+'*' Literal.String
+':' Punctuation
+'17110' Literal.Number
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'99999' Literal.Number
+':' Punctuation
+'7' Literal.Number
+':' Punctuation
+':' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+'user' Text
+':' Punctuation
+'$6$KmghZnvbZs7f3SQ9$H6f0M61q5Cf8JLrS0kR3M97/o6GzD6FH3MbLs92CM/l9mHZ7FngBzRfa8D5NrWl.K8nM64affeWrY/L0U7nBt/' Literal.String
+':' Punctuation
+'19097' Literal.Number
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'99999' Literal.Number
+':' Punctuation
+'7' Literal.Number
+':' Punctuation
+':' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
+
+'linoadmin' Text
+':' Punctuation
+'!!' Literal.String
+':' Punctuation
+'17289' Literal.Number
+':' Punctuation
+'0' Literal.Number
+':' Punctuation
+'99999' Literal.Number
+':' Punctuation
+'7' Literal.Number
+':' Punctuation
+':' Punctuation
+':' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_attribute.txt b/tests/snippets/usd/test_attribute.txt
new file mode 100644
index 0000000..74e6789
--- /dev/null
+++ b/tests/snippets/usd/test_attribute.txt
@@ -0,0 +1,174 @@
+---input---
+double foo = 8.0
+
+custom double foo = 8.0
+
+uniform double foo = 8.0
+
+custom uniform double foo = 8.0
+
+custom double foo_underscore_name = 8.0
+
+double[] foo_underscore_name = [10.1, 12.0, 13]
+
+double[] primvar:foo_thing = [10.1, 12.0, 13]
+
+custom int[] foo = [8, 10, 14]
+
+custom int[] foo.timeSamples = {
+ 1: [8, 0, 14],
+ 2: [-8, 0, 14],
+}
+
+---tokens---
+'double' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'8.0' Literal.Number
+'\n\n' Text.Whitespace
+
+'custom' Keyword.Token
+' ' Text.Whitespace
+'double' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'8.0' Literal.Number
+'\n\n' Text.Whitespace
+
+'uniform' Keyword.Token
+' ' Text.Whitespace
+'double' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'8.0' Literal.Number
+'\n\n' Text.Whitespace
+
+'custom' Keyword.Token
+' ' Text.Whitespace
+'uniform' Keyword.Token
+' ' Text.Whitespace
+'double' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'8.0' Literal.Number
+'\n\n' Text.Whitespace
+
+'custom' Keyword.Token
+' ' Text.Whitespace
+'double' Keyword.Type
+' ' Text.Whitespace
+'foo_underscore_name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'8.0' Literal.Number
+'\n\n' Text.Whitespace
+
+'double[]' Keyword.Type
+' ' Text.Whitespace
+'foo_underscore_name' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'10.1' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'12.0' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'13' Literal.Number
+']' Punctuation
+'\n\n' Text.Whitespace
+
+'double[]' Keyword.Type
+' ' Text.Whitespace
+'primvar:foo_thing' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'10.1' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'12.0' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'13' Literal.Number
+']' Punctuation
+'\n\n' Text.Whitespace
+
+'custom' Keyword.Token
+' ' Text.Whitespace
+'int[]' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'8' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'10' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'14' Literal.Number
+']' Punctuation
+'\n\n' Text.Whitespace
+
+'custom' Keyword.Token
+' ' Text.Whitespace
+'int[]' Keyword.Type
+' ' Text.Whitespace
+'foo' Name.Attribute
+'.' Text
+'timeSamples' Name.Keyword.Tokens
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'1' Literal.Number
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'8' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'14' Literal.Number
+']' Punctuation
+',' Punctuation
+'\n ' Text.Whitespace
+'2' Literal.Number
+':' Punctuation
+' ' Text.Whitespace
+'[' Punctuation
+'-8' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'0' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'14' Literal.Number
+']' Punctuation
+',' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_composition_arcs.txt b/tests/snippets/usd/test_composition_arcs.txt
new file mode 100644
index 0000000..0270c93
--- /dev/null
+++ b/tests/snippets/usd/test_composition_arcs.txt
@@ -0,0 +1,101 @@
+---input---
+def Xform "BottleMedical" (
+ kind = "prop"
+ payload = @./BottleMedical_payload.usd@</BottleMedical>
+ variants = {
+ string modelingVariant = "LiquidBottleLg"
+ string shadingComplexity = "full"
+ }
+ add variantSets = ["modelingVariant", "shadingComplexity"]
+)
+{
+ variantSet "modelingVariant" = {
+ "ALL_VARIANTS" {
+ }
+ }
+}
+
+---tokens---
+'def' Keyword.Tokens
+' ' Text.Whitespace
+'Xform' Text
+' ' Text.Whitespace
+'"BottleMedical"' Literal.String
+' ' Text.Whitespace
+'(' Punctuation
+'\n ' Text.Whitespace
+'kind' Name.Builtins
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"prop"' Literal.String
+'\n ' Text.Whitespace
+'payload' Keyword.Tokens
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'@./BottleMedical_payload.usd@' Literal.String.Interpol
+'</BottleMedical>' Name.Namespace
+'\n ' Text.Whitespace
+'variants' Keyword.Tokens
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'string' Keyword.Type
+' ' Text.Whitespace
+'modelingVariant' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"LiquidBottleLg"' Literal.String
+'\n ' Text.Whitespace
+'string' Keyword.Type
+' ' Text.Whitespace
+'shadingComplexity' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"full"' Literal.String
+'\n ' Text.Whitespace
+'}' Punctuation
+'\n ' Text.Whitespace
+'add' Keyword.Type
+' ' Text.Whitespace
+'variantSets' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'"modelingVariant"' Literal.String
+',' Punctuation
+' ' Text.Whitespace
+'"shadingComplexity"' Literal.String
+']' Punctuation
+'\n' Text.Whitespace
+
+')' Punctuation
+'\n' Text.Whitespace
+
+'{' Punctuation
+'\n ' Text.Whitespace
+'variantSet' Keyword.Tokens
+' ' Text.Whitespace
+'"modelingVariant"' Literal.String
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'"ALL_VARIANTS"' Literal.String
+' ' Text.Whitespace
+'{' Punctuation
+'\n ' Text.Whitespace
+'}' Punctuation
+'\n ' Text.Whitespace
+'}' Punctuation
+'\n' Text.Whitespace
+
+'}' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_metadata.txt b/tests/snippets/usd/test_metadata.txt
new file mode 100644
index 0000000..edc4614
--- /dev/null
+++ b/tests/snippets/usd/test_metadata.txt
@@ -0,0 +1,36 @@
+# Make sure metadata [the stuff inside ()s] don't match as Attributes.
+
+---input---
+float[] primvars:skel:jointWeights = [1] (
+ elementSize = 1
+ interpolation = "constant"
+)
+
+---tokens---
+'float[]' Keyword.Type
+' ' Text.Whitespace
+'primvars:skel:jointWeights' Name.Attribute
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'[' Punctuation
+'1' Literal.Number
+']' Punctuation
+' ' Text.Whitespace
+'(' Punctuation
+'\n ' Text.Whitespace
+'elementSize' Name.Builtins
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'1' Literal.Number
+'\n ' Text.Whitespace
+'interpolation' Name.Builtins
+' ' Text.Whitespace
+'=' Operator
+' ' Text.Whitespace
+'"constant"' Literal.String
+'\n' Text.Whitespace
+
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_numbers.txt b/tests/snippets/usd/test_numbers.txt
new file mode 100644
index 0000000..a4e0f88
--- /dev/null
+++ b/tests/snippets/usd/test_numbers.txt
@@ -0,0 +1,21 @@
+---input---
+8 8.0123312132, -4 -14.123 1e10 0.1e10 10.123e+10 0.123e-14
+
+---tokens---
+'8' Literal.Number
+' ' Text.Whitespace
+'8.0123312132' Literal.Number
+',' Punctuation
+' ' Text.Whitespace
+'-4' Literal.Number
+' ' Text.Whitespace
+'-14.123' Literal.Number
+' ' Text.Whitespace
+'1e10' Literal.Number
+' ' Text.Whitespace
+'0.1e10' Literal.Number
+' ' Text.Whitespace
+'10.123e+10' Literal.Number
+' ' Text.Whitespace
+'0.123e-14' Literal.Number
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_outer_match_at_sign.txt b/tests/snippets/usd/test_outer_match_at_sign.txt
new file mode 100644
index 0000000..de0bc72
--- /dev/null
+++ b/tests/snippets/usd/test_outer_match_at_sign.txt
@@ -0,0 +1,14 @@
+# Make sure that text between located between quotes and @@s are not matched.
+
+---input---
+@firststring@ something else @secondstring@
+
+---tokens---
+'@firststring@' Literal.String.Interpol
+' ' Text.Whitespace
+'something' Text
+' ' Text.Whitespace
+'else' Text
+' ' Text.Whitespace
+'@secondstring@' Literal.String.Interpol
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_outer_match_double.txt b/tests/snippets/usd/test_outer_match_double.txt
new file mode 100644
index 0000000..773da13
--- /dev/null
+++ b/tests/snippets/usd/test_outer_match_double.txt
@@ -0,0 +1,12 @@
+---input---
+'firststring' something else 'secondstring'
+
+---tokens---
+"'firststring'" Literal.String
+' ' Text.Whitespace
+'something' Text
+' ' Text.Whitespace
+'else' Text
+' ' Text.Whitespace
+"'secondstring'" Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_outer_match_single.txt b/tests/snippets/usd/test_outer_match_single.txt
new file mode 100644
index 0000000..773da13
--- /dev/null
+++ b/tests/snippets/usd/test_outer_match_single.txt
@@ -0,0 +1,12 @@
+---input---
+'firststring' something else 'secondstring'
+
+---tokens---
+"'firststring'" Literal.String
+' ' Text.Whitespace
+'something' Text
+' ' Text.Whitespace
+'else' Text
+' ' Text.Whitespace
+"'secondstring'" Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_string_multiple_line.txt b/tests/snippets/usd/test_string_multiple_line.txt
new file mode 100644
index 0000000..b3ab1fd
--- /dev/null
+++ b/tests/snippets/usd/test_string_multiple_line.txt
@@ -0,0 +1,20 @@
+---input---
+"""
+Some text multiline
+"""
+
+"""Some text multiline
+"""
+
+"""
+Some text multiline"""
+
+---tokens---
+'"""\nSome text multiline\n"""' Literal.String
+'\n\n' Text.Whitespace
+
+'"""Some text multiline\n"""' Literal.String
+'\n\n' Text.Whitespace
+
+'"""\nSome text multiline"""' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_string_priority.txt b/tests/snippets/usd/test_string_priority.txt
new file mode 100644
index 0000000..481a416
--- /dev/null
+++ b/tests/snippets/usd/test_string_priority.txt
@@ -0,0 +1,10 @@
+# Make sure that no other rules override a string match.
+
+---input---
+"""
+custom int[] foo = [8, 10, 14]
+"""
+
+---tokens---
+'"""\ncustom int[] foo = [8, 10, 14]\n"""' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/usd/test_string_single_line.txt b/tests/snippets/usd/test_string_single_line.txt
new file mode 100644
index 0000000..b5058ce
--- /dev/null
+++ b/tests/snippets/usd/test_string_single_line.txt
@@ -0,0 +1,6 @@
+---input---
+"Some 'text"
+
+---tokens---
+'"Some \'text"' Literal.String
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_floats.txt b/tests/snippets/vbscript/test_floats.txt
new file mode 100644
index 0000000..9493a3b
--- /dev/null
+++ b/tests/snippets/vbscript/test_floats.txt
@@ -0,0 +1,34 @@
+---input---
+1.
+1.e1
+.1
+1.2
+1.2e3
+1.2e+3
+1.2e-3
+1e2
+
+---tokens---
+'1.' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.e1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'.1' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.2' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.2e3' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.2e+3' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1.2e-3' Literal.Number.Float
+'\n' Text.Whitespace
+
+'1e2' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_floats_multiple.txt b/tests/snippets/vbscript/test_floats_multiple.txt
new file mode 100644
index 0000000..30a3708
--- /dev/null
+++ b/tests/snippets/vbscript/test_floats_multiple.txt
@@ -0,0 +1,7 @@
+---input---
+1e2.1e2
+
+---tokens---
+'1e2' Literal.Number.Float
+'.1e2' Literal.Number.Float
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_integers.txt b/tests/snippets/vbscript/test_integers.txt
new file mode 100644
index 0000000..132ef7e
--- /dev/null
+++ b/tests/snippets/vbscript/test_integers.txt
@@ -0,0 +1,14 @@
+---input---
+1
+23
+456
+
+---tokens---
+'1' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'23' Literal.Number.Integer
+'\n' Text.Whitespace
+
+'456' Literal.Number.Integer
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_invalid_character.txt b/tests/snippets/vbscript/test_invalid_character.txt
new file mode 100644
index 0000000..6a1e6f1
--- /dev/null
+++ b/tests/snippets/vbscript/test_invalid_character.txt
@@ -0,0 +1,10 @@
+---input---
+a;bc
+d
+
+---tokens---
+'a' Name
+';bc\n' Error
+
+'d' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_names.txt b/tests/snippets/vbscript/test_names.txt
new file mode 100644
index 0000000..404844f
--- /dev/null
+++ b/tests/snippets/vbscript/test_names.txt
@@ -0,0 +1,18 @@
+---input---
+thingy
+thingy123
+_thingy
+_123
+
+---tokens---
+'thingy' Name
+'\n' Text.Whitespace
+
+'thingy123' Name
+'\n' Text.Whitespace
+
+'_thingy' Name
+'\n' Text.Whitespace
+
+'_123' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_reject_almost_float.txt b/tests/snippets/vbscript/test_reject_almost_float.txt
new file mode 100644
index 0000000..478e6a3
--- /dev/null
+++ b/tests/snippets/vbscript/test_reject_almost_float.txt
@@ -0,0 +1,7 @@
+---input---
+.e1
+
+---tokens---
+'.' Punctuation
+'e1' Name
+'\n' Text.Whitespace
diff --git a/tests/snippets/vbscript/test_unterminated_string.txt b/tests/snippets/vbscript/test_unterminated_string.txt
new file mode 100644
index 0000000..e92060b
--- /dev/null
+++ b/tests/snippets/vbscript/test_unterminated_string.txt
@@ -0,0 +1,7 @@
+---input---
+"x\nx
+
+---tokens---
+'"' Literal.String.Double
+'x\\nx' Literal.String.Double
+'\n' Error
diff --git a/tests/snippets/wat/test_align_and_offset_accept_hexadecimal_numbers.txt b/tests/snippets/wat/test_align_and_offset_accept_hexadecimal_numbers.txt
new file mode 100644
index 0000000..919e1d2
--- /dev/null
+++ b/tests/snippets/wat/test_align_and_offset_accept_hexadecimal_numbers.txt
@@ -0,0 +1,14 @@
+---input---
+i32.store offset=0xdeadbeef align=0x1
+
+---tokens---
+'i32.store' Name.Builtin
+' ' Text
+'offset' Keyword
+'=' Operator
+'0xdeadbeef' Literal.Number.Hex
+' ' Text
+'align' Keyword
+'=' Operator
+'0x1' Literal.Number.Hex
+'\n' Text
diff --git a/tests/snippets/wat/test_comment_with_open_paren.txt b/tests/snippets/wat/test_comment_with_open_paren.txt
new file mode 100644
index 0000000..631de4c
--- /dev/null
+++ b/tests/snippets/wat/test_comment_with_open_paren.txt
@@ -0,0 +1,10 @@
+---input---
+(; comment with ( open paren ;)
+
+---tokens---
+'(;' Comment.Multiline
+' comment with ' Comment.Multiline
+'(' Comment.Multiline
+' open paren ' Comment.Multiline
+';)' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/wat/test_comment_with_semicolon.txt b/tests/snippets/wat/test_comment_with_semicolon.txt
new file mode 100644
index 0000000..0cd3112
--- /dev/null
+++ b/tests/snippets/wat/test_comment_with_semicolon.txt
@@ -0,0 +1,10 @@
+---input---
+(; comment with ; semicolon ;)
+
+---tokens---
+'(;' Comment.Multiline
+' comment with ' Comment.Multiline
+';' Comment.Multiline
+' semicolon ' Comment.Multiline
+';)' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/wat/test_i32_const_is_builtin.txt b/tests/snippets/wat/test_i32_const_is_builtin.txt
new file mode 100644
index 0000000..740907c
--- /dev/null
+++ b/tests/snippets/wat/test_i32_const_is_builtin.txt
@@ -0,0 +1,6 @@
+---input---
+i32.const
+
+---tokens---
+'i32.const' Name.Builtin
+'\n' Text
diff --git a/tests/snippets/wat/test_multiline_comment.txt b/tests/snippets/wat/test_multiline_comment.txt
new file mode 100644
index 0000000..6cbd45e
--- /dev/null
+++ b/tests/snippets/wat/test_multiline_comment.txt
@@ -0,0 +1,11 @@
+---input---
+(;
+ comment
+;)
+
+---tokens---
+'(;' Comment.Multiline
+'\n comment\n' Comment.Multiline
+
+';)' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/wat/test_nested_comment.txt b/tests/snippets/wat/test_nested_comment.txt
new file mode 100644
index 0000000..de07293
--- /dev/null
+++ b/tests/snippets/wat/test_nested_comment.txt
@@ -0,0 +1,14 @@
+---input---
+(;
+nested(;;)comment
+;)
+
+---tokens---
+'(;' Comment.Multiline
+'\nnested' Comment.Multiline
+'(;' Comment.Multiline
+';)' Comment.Multiline
+'comment\n' Comment.Multiline
+
+';)' Comment.Multiline
+'\n' Text
diff --git a/tests/snippets/wat/test_string_byte_escape.txt b/tests/snippets/wat/test_string_byte_escape.txt
new file mode 100644
index 0000000..c0b9e4a
--- /dev/null
+++ b/tests/snippets/wat/test_string_byte_escape.txt
@@ -0,0 +1,9 @@
+---input---
+"\001"
+
+---tokens---
+'"' Literal.String.Double
+'\\00' Literal.String.Escape
+'1' Literal.String.Double
+'"' Literal.String.Double
+'\n' Text
diff --git a/tests/snippets/wat/test_string_with_escape.txt b/tests/snippets/wat/test_string_with_escape.txt
new file mode 100644
index 0000000..c978faa
--- /dev/null
+++ b/tests/snippets/wat/test_string_with_escape.txt
@@ -0,0 +1,9 @@
+---input---
+"string\t"
+
+---tokens---
+'"' Literal.String.Double
+'string' Literal.String.Double
+'\\t' Literal.String.Escape
+'"' Literal.String.Double
+'\n' Text
diff --git a/tests/snippets/wat/test_variable_name_pattern.txt b/tests/snippets/wat/test_variable_name_pattern.txt
new file mode 100644
index 0000000..d305ab9
--- /dev/null
+++ b/tests/snippets/wat/test_variable_name_pattern.txt
@@ -0,0 +1,6 @@
+---input---
+$ABCabc123!#$%&'*+./:<=>?@\\^_`|~-A
+
+---tokens---
+"$ABCabc123!#$%&'*+./:<=>?@\\\\^_`|~-A" Name.Variable
+'\n' Text
diff --git a/tests/snippets/whiley/test_whiley_operator.txt b/tests/snippets/whiley/test_whiley_operator.txt
new file mode 100644
index 0000000..50761db
--- /dev/null
+++ b/tests/snippets/whiley/test_whiley_operator.txt
@@ -0,0 +1,10 @@
+---input---
+123 ∀ x
+
+---tokens---
+'123' Literal.Number.Integer
+' ' Text
+'∀' Operator
+' ' Text
+'x' Name
+'\n' Text
diff --git a/tests/snippets/wren/lonely-paren.txt b/tests/snippets/wren/lonely-paren.txt
new file mode 100644
index 0000000..5236e60
--- /dev/null
+++ b/tests/snippets/wren/lonely-paren.txt
@@ -0,0 +1,10 @@
+---input---
+// This invalid input should terminate.
+)
+
+---tokens---
+'// This invalid input should terminate.' Comment.Single
+'\n' Text.Whitespace
+
+')' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/xml/multiline-comment-catastrophic-backtracking.txt b/tests/snippets/xml/multiline-comment-catastrophic-backtracking.txt
new file mode 100644
index 0000000..d9a8b7a
--- /dev/null
+++ b/tests/snippets/xml/multiline-comment-catastrophic-backtracking.txt
@@ -0,0 +1,56 @@
+---input---
+<!--
+this
+comment
+is
+never
+terminated
+...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+...
+
+---tokens---
+'<' Error
+'!--' Text
+'\n' Text.Whitespace
+
+'this' Text
+'\n' Text.Whitespace
+
+'comment' Text
+'\n' Text.Whitespace
+
+'is' Text
+'\n' Text.Whitespace
+
+'never' Text
+'\n' Text.Whitespace
+
+'terminated' Text
+'\n' Text.Whitespace
+
+'...' Text
+'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' Text.Whitespace
+
+'...' Text
+'\n' Text.Whitespace
diff --git a/tests/snippets/yaml/test_yaml.txt b/tests/snippets/yaml/test_yaml.txt
new file mode 100644
index 0000000..0dd3911
--- /dev/null
+++ b/tests/snippets/yaml/test_yaml.txt
@@ -0,0 +1,13 @@
+# Bug #1528: This previously parsed 'token # innocent' as a tag
+
+---input---
+here: token # innocent: comment
+
+---tokens---
+'here' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'token' Literal.Scalar.Plain
+' ' Text.Whitespace
+'# innocent: comment' Comment.Single
+'\n' Text.Whitespace
diff --git a/tests/snippets/yaml/test_yaml_colon_in_key.txt b/tests/snippets/yaml/test_yaml_colon_in_key.txt
new file mode 100644
index 0000000..9f3d313
--- /dev/null
+++ b/tests/snippets/yaml/test_yaml_colon_in_key.txt
@@ -0,0 +1,11 @@
+# Colon in the key name is accepted by the YAML specs too
+
+---input---
+foo:bar: value
+
+---tokens---
+'foo:bar' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'value' Literal.Scalar.Plain
+'\n' Text.Whitespace
diff --git a/tests/snippets/yaml/test_yaml_colon_in_key_double.txt b/tests/snippets/yaml/test_yaml_colon_in_key_double.txt
new file mode 100644
index 0000000..6f68b6d
--- /dev/null
+++ b/tests/snippets/yaml/test_yaml_colon_in_key_double.txt
@@ -0,0 +1,11 @@
+# Colons in the key name is accepted by the YAML specs too
+
+---input---
+foo::bar: value
+
+---tokens---
+'foo::bar' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'value' Literal.Scalar.Plain
+'\n' Text.Whitespace
diff --git a/tests/snippets/yaml/test_yaml_colon_in_key_start.txt b/tests/snippets/yaml/test_yaml_colon_in_key_start.txt
new file mode 100644
index 0000000..60d4e23
--- /dev/null
+++ b/tests/snippets/yaml/test_yaml_colon_in_key_start.txt
@@ -0,0 +1,11 @@
+# Colon at the beginning of the key name is accepted by the YAML specs too
+
+---input---
+:foo: value
+
+---tokens---
+':foo' Name.Tag
+':' Punctuation
+' ' Text.Whitespace
+'value' Literal.Scalar.Plain
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_float_value.txt b/tests/snippets/yang/test_float_value.txt
new file mode 100644
index 0000000..b49f479
--- /dev/null
+++ b/tests/snippets/yang/test_float_value.txt
@@ -0,0 +1,11 @@
+# Float value `1.1` should be explicitly highlighted
+
+---input---
+yang-version 1.1;
+
+---tokens---
+'yang-version' Keyword
+' ' Text.Whitespace
+'1.1' Literal.Number.Float
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_integer_value.txt b/tests/snippets/yang/test_integer_value.txt
new file mode 100644
index 0000000..149d19c
--- /dev/null
+++ b/tests/snippets/yang/test_integer_value.txt
@@ -0,0 +1,11 @@
+# Integer value `5` should be explicitly highlighted
+
+---input---
+value 5;
+
+---tokens---
+'value' Keyword
+' ' Text.Whitespace
+'5' Literal.Number.Integer
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_namespace_1.txt b/tests/snippets/yang/test_namespace_1.txt
new file mode 100644
index 0000000..1caf138
--- /dev/null
+++ b/tests/snippets/yang/test_namespace_1.txt
@@ -0,0 +1,11 @@
+# Namespace `urn:test:std:yang` should not be explicitly highlighted
+
+---input---
+namespace urn:test:std:yang;
+
+---tokens---
+'namespace' Keyword
+' ' Text.Whitespace
+'urn:test:std:yang' Name.Variable
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_namespace_2.txt b/tests/snippets/yang/test_namespace_2.txt
new file mode 100644
index 0000000..a245f7c
--- /dev/null
+++ b/tests/snippets/yang/test_namespace_2.txt
@@ -0,0 +1,13 @@
+# namespace-prefix `yang` should be explicitly highlighted
+
+---input---
+type yang:counter64;
+
+---tokens---
+'type' Keyword
+' ' Text.Whitespace
+'yang' Name.Namespace
+':' Punctuation
+'counter64' Name.Variable
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_revision_date.txt b/tests/snippets/yang/test_revision_date.txt
new file mode 100644
index 0000000..09ff5a6
--- /dev/null
+++ b/tests/snippets/yang/test_revision_date.txt
@@ -0,0 +1,11 @@
+# Revision-date `2020-08-03` should be explicitly highlighted
+
+---input---
+revision 2020-03-08{
+
+---tokens---
+'revision' Keyword
+' ' Text.Whitespace
+'2020-03-08' Name.Label
+'{' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/snippets/yang/test_string_value.txt b/tests/snippets/yang/test_string_value.txt
new file mode 100644
index 0000000..41dcae5
--- /dev/null
+++ b/tests/snippets/yang/test_string_value.txt
@@ -0,0 +1,11 @@
+# String value `"5"` should be not explicitly highlighted
+
+---input---
+value "5";
+
+---tokens---
+'value' Keyword
+' ' Text.Whitespace
+'"5"' Literal.String.Double
+';' Punctuation
+'\n' Text.Whitespace
diff --git a/tests/support/empty.py b/tests/support/empty.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/support/empty.py
diff --git a/tests/support/html_formatter.py b/tests/support/html_formatter.py
new file mode 100644
index 0000000..5f04fd5
--- /dev/null
+++ b/tests/support/html_formatter.py
@@ -0,0 +1,5 @@
+from pygments.formatters import HtmlFormatter
+
+
+class HtmlFormatterWrapper(HtmlFormatter):
+ name = 'HtmlWrapper'
diff --git a/tests/support/python_lexer.py b/tests/support/python_lexer.py
new file mode 100644
index 0000000..78d9c4a
--- /dev/null
+++ b/tests/support/python_lexer.py
@@ -0,0 +1,11 @@
+# pygments.lexers.python (as CustomLexer) for test_cmdline.py
+
+from pygments.lexers import PythonLexer
+
+
+class CustomLexer(PythonLexer):
+ name = 'PythonLexerWrapper'
+
+
+class LexerWrapper(CustomLexer):
+ name = 'PythonLexerWrapperWrapper'
diff --git a/tests/support/structural_diff.py b/tests/support/structural_diff.py
new file mode 100644
index 0000000..cea27d1
--- /dev/null
+++ b/tests/support/structural_diff.py
@@ -0,0 +1,37 @@
+import html.parser
+
+
+class Parser(html.parser.HTMLParser):
+ def __init__(self):
+ super().__init__()
+ self._stream = []
+
+ def handle_starttag(self, tag, attrs):
+ attrs = sorted(attrs, key=lambda x: x[0])
+ attrs = '|'.join([k[0] + ':' + k[1] for k in attrs])
+ self._stream.append(('<', tag, attrs))
+
+ def handle_endtag(self, tag):
+ self._stream.append(('>', tag, ''))
+
+ def handle_data(self, data):
+ self._stream.append(('_', data, ''))
+
+ @property
+ def stream(self):
+ return self._stream
+
+
+def _serialize(t):
+ parser = Parser()
+ parser.feed(t)
+ return parser.stream
+
+
+def structural_diff(a, b):
+ """Check if there is a structural difference between two HTML files."""
+ a_s = _serialize(a)
+ b_s = _serialize(b)
+
+ for e, f in zip(a_s, b_s):
+ assert e == f, f'Expected: {e}, found: {f}'
diff --git a/tests/support/tags b/tests/support/tags
new file mode 100644
index 0000000..193779f
--- /dev/null
+++ b/tests/support/tags
@@ -0,0 +1,36 @@
+!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME Exuberant Ctags //
+!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
+!_TAG_PROGRAM_VERSION 5.8 //
+HtmlFormatter test_html_formatter.py 19;" i
+HtmlFormatterTest test_html_formatter.py 34;" c
+NullFormatter test_html_formatter.py 19;" i
+PythonLexer test_html_formatter.py 18;" i
+StringIO test_html_formatter.py 13;" i
+dirname test_html_formatter.py 16;" i
+escape_html test_html_formatter.py 20;" i
+fp test_html_formatter.py 27;" v
+inspect test_html_formatter.py 15;" i
+isfile test_html_formatter.py 16;" i
+join test_html_formatter.py 16;" i
+os test_html_formatter.py 10;" i
+re test_html_formatter.py 11;" i
+subprocess test_html_formatter.py 125;" i
+support test_html_formatter.py 23;" i
+tempfile test_html_formatter.py 14;" i
+test_all_options test_html_formatter.py 72;" m class:HtmlFormatterTest
+test_correct_output test_html_formatter.py 35;" m class:HtmlFormatterTest
+test_ctags test_html_formatter.py 165;" m class:HtmlFormatterTest
+test_external_css test_html_formatter.py 48;" m class:HtmlFormatterTest
+test_get_style_defs test_html_formatter.py 141;" m class:HtmlFormatterTest
+test_lineanchors test_html_formatter.py 98;" m class:HtmlFormatterTest
+test_lineanchors_with_startnum test_html_formatter.py 106;" m class:HtmlFormatterTest
+test_linenos test_html_formatter.py 82;" m class:HtmlFormatterTest
+test_linenos_with_startnum test_html_formatter.py 90;" m class:HtmlFormatterTest
+test_unicode_options test_html_formatter.py 155;" m class:HtmlFormatterTest
+test_valid_output test_html_formatter.py 114;" m class:HtmlFormatterTest
+tokensource test_html_formatter.py 29;" v
+uni_open test_html_formatter.py 21;" i
+unittest test_html_formatter.py 12;" i
diff --git a/tests/test_basic_api.py b/tests/test_basic_api.py
new file mode 100644
index 0000000..62ea489
--- /dev/null
+++ b/tests/test_basic_api.py
@@ -0,0 +1,351 @@
+"""
+ Pygments basic API tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import random
+from io import StringIO, BytesIO
+from os import path
+
+import pytest
+
+from pygments import lexers, formatters, lex, format
+from pygments.token import _TokenType, Text
+from pygments.lexer import RegexLexer
+from pygments.formatter import Formatter
+from pygments.formatters.img import FontNotFound
+from pygments.util import ClassNotFound
+
+TESTDIR = path.dirname(path.abspath(__file__))
+TESTFILE = path.join(TESTDIR, 'test_basic_api.py')
+
+test_content = [chr(i) for i in range(33, 128)] * 5
+random.shuffle(test_content)
+test_content = ''.join(test_content) + '\n'
+
+
+@pytest.mark.parametrize('name', lexers.LEXERS)
+def test_lexer_instantiate_all(name):
+ # instantiate every lexer, to see if the token type defs are correct
+ getattr(lexers, name)
+
+
+@pytest.mark.parametrize('cls', lexers._iter_lexerclasses(plugins=False))
+def test_lexer_classes(cls):
+ # test that every lexer class has the correct public API
+ assert type(cls.name) is str
+ for attr in 'aliases', 'filenames', 'alias_filenames', 'mimetypes':
+ assert hasattr(cls, attr)
+ assert type(getattr(cls, attr)) is list, \
+ "%s: %s attribute wrong" % (cls, attr)
+ result = cls.analyse_text("abc")
+ assert isinstance(result, float) and 0.0 <= result <= 1.0
+ result = cls.analyse_text(".abc")
+ assert isinstance(result, float) and 0.0 <= result <= 1.0
+
+ assert all(al.lower() == al for al in cls.aliases)
+
+ if issubclass(cls, RegexLexer):
+ inst = cls(opt1="val1", opt2="val2")
+ if not hasattr(cls, '_tokens'):
+ # if there's no "_tokens", the lexer has to be one with
+ # multiple tokendef variants
+ assert cls.token_variants
+ for variant in cls.tokens:
+ assert 'root' in cls.tokens[variant]
+ else:
+ assert 'root' in cls._tokens, \
+ '%s has no root state' % cls
+
+
+@pytest.mark.parametrize('cls', lexers._iter_lexerclasses(plugins=False))
+def test_random_input(cls):
+ inst = cls()
+ try:
+ tokens = list(inst.get_tokens(test_content))
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt(
+ 'interrupted %s.get_tokens(): test_content=%r' %
+ (cls.__name__, test_content))
+ txt = ""
+ for token in tokens:
+ assert isinstance(token, tuple)
+ assert isinstance(token[0], _TokenType)
+ assert isinstance(token[1], str)
+ txt += token[1]
+ assert txt == test_content, "%s lexer roundtrip failed: %r != %r" % \
+ (cls.name, test_content, txt)
+
+
+@pytest.mark.parametrize('cls', lexers._iter_lexerclasses(plugins=False))
+def test_lexer_options(cls):
+ if cls.__name__ == 'RawTokenLexer':
+ # this one is special
+ return
+
+ # test that the basic options work
+ def ensure(tokens, output):
+ concatenated = ''.join(token[1] for token in tokens)
+ assert concatenated == output, \
+ '%s: %r != %r' % (cls, concatenated, output)
+
+ inst = cls(stripnl=False)
+ ensure(inst.get_tokens('a\nb'), 'a\nb\n')
+ ensure(inst.get_tokens('\n\n\n'), '\n\n\n')
+ inst = cls(stripall=True)
+ ensure(inst.get_tokens(' \n b\n\n\n'), 'b\n')
+ # some lexers require full lines in input
+ if ('ConsoleLexer' not in cls.__name__ and
+ 'SessionLexer' not in cls.__name__ and
+ not cls.__name__.startswith('Literate') and
+ cls.__name__ not in ('ErlangShellLexer', 'RobotFrameworkLexer')):
+ inst = cls(ensurenl=False)
+ ensure(inst.get_tokens('a\nb'), 'a\nb')
+ inst = cls(ensurenl=False, stripall=True)
+ ensure(inst.get_tokens('a\nb\n\n'), 'a\nb')
+
+
+def test_get_lexers():
+ # test that the lexers functions work
+ for func, args in [(lexers.get_lexer_by_name, ("python",)),
+ (lexers.get_lexer_for_filename, ("test.py",)),
+ (lexers.get_lexer_for_mimetype, ("text/x-python",)),
+ (lexers.guess_lexer, ("#!/usr/bin/python3 -O\nprint",)),
+ (lexers.guess_lexer_for_filename, ("a.py", "<%= @foo %>"))
+ ]:
+ x = func(opt='val', *args)
+ assert isinstance(x, lexers.PythonLexer)
+ assert x.options["opt"] == "val"
+
+ for cls, (_, lname, aliases, _, mimetypes) in lexers.LEXERS.items():
+ assert cls == lexers.find_lexer_class(lname).__name__
+
+ for alias in aliases:
+ assert cls == lexers.get_lexer_by_name(alias).__class__.__name__
+
+ for mimetype in mimetypes:
+ assert cls == lexers.get_lexer_for_mimetype(mimetype).__class__.__name__
+
+ try:
+ lexers.get_lexer_by_name(None)
+ except ClassNotFound:
+ pass
+ else:
+ raise Exception
+
+
+@pytest.mark.parametrize('cls', [getattr(formatters, name)
+ for name in formatters.FORMATTERS])
+def test_formatter_public_api(cls):
+ # test that every formatter class has the correct public API
+ ts = list(lexers.PythonLexer().get_tokens("def f(): pass"))
+ string_out = StringIO()
+ bytes_out = BytesIO()
+
+ info = formatters.FORMATTERS[cls.__name__]
+ assert len(info) == 5
+ assert info[1], "missing formatter name"
+ assert info[2], "missing formatter aliases"
+ assert info[4], "missing formatter docstring"
+
+ try:
+ inst = cls(opt1="val1")
+ except (ImportError, FontNotFound) as e:
+ pytest.skip(str(e))
+
+ try:
+ inst.get_style_defs()
+ except NotImplementedError:
+ # may be raised by formatters for which it doesn't make sense
+ pass
+
+ if cls.unicodeoutput:
+ inst.format(ts, string_out)
+ else:
+ inst.format(ts, bytes_out)
+
+
+def test_formatter_encodings():
+ from pygments.formatters import HtmlFormatter
+
+ # unicode output
+ fmt = HtmlFormatter()
+ tokens = [(Text, "ä")]
+ out = format(tokens, fmt)
+ assert type(out) is str
+ assert "ä" in out
+
+ # encoding option
+ fmt = HtmlFormatter(encoding="latin1")
+ tokens = [(Text, "ä")]
+ assert "ä".encode("latin1") in format(tokens, fmt)
+
+ # encoding and outencoding option
+ fmt = HtmlFormatter(encoding="latin1", outencoding="utf8")
+ tokens = [(Text, "ä")]
+ assert "ä".encode() in format(tokens, fmt)
+
+
+@pytest.mark.parametrize('cls', [getattr(formatters, name)
+ for name in formatters.FORMATTERS])
+def test_formatter_unicode_handling(cls):
+ # test that the formatter supports encoding and Unicode
+ tokens = list(lexers.PythonLexer(encoding='utf-8').
+ get_tokens("def f(): 'ä'"))
+
+ try:
+ inst = cls(encoding=None)
+ except (ImportError, FontNotFound) as e:
+ # some dependency or font not installed
+ pytest.skip(str(e))
+
+ if cls.name != 'Raw tokens':
+ out = format(tokens, inst)
+ if cls.unicodeoutput:
+ assert type(out) is str, '%s: %r' % (cls, out)
+
+ inst = cls(encoding='utf-8')
+ out = format(tokens, inst)
+ assert type(out) is bytes, '%s: %r' % (cls, out)
+ # Cannot test for encoding, since formatters may have to escape
+ # non-ASCII characters.
+ else:
+ inst = cls()
+ out = format(tokens, inst)
+ assert type(out) is bytes, '%s: %r' % (cls, out)
+
+
+def test_get_formatters():
+ # test that the formatters functions work
+ x = formatters.get_formatter_by_name("html", opt="val")
+ assert isinstance(x, formatters.HtmlFormatter)
+ assert x.options["opt"] == "val"
+
+ x = formatters.get_formatter_for_filename("a.html", opt="val")
+ assert isinstance(x, formatters.HtmlFormatter)
+ assert x.options["opt"] == "val"
+
+
+def test_styles():
+ # minimal style test
+ from pygments.formatters import HtmlFormatter
+ HtmlFormatter(style="pastie")
+
+
+def test_bare_class_handler():
+ from pygments.formatters import HtmlFormatter
+ from pygments.lexers import PythonLexer
+ try:
+ lex('test\n', PythonLexer)
+ except TypeError as e:
+ assert 'lex() argument must be a lexer instance' in str(e)
+ else:
+ assert False, 'nothing raised'
+ try:
+ format([], HtmlFormatter)
+ except TypeError as e:
+ assert 'format() argument must be a formatter instance' in str(e)
+ else:
+ assert False, 'nothing raised'
+
+ # These cases should not trigger this heuristic.
+ class BuggyLexer(RegexLexer):
+ def get_tokens(self, text, extra_argument):
+ pass
+ tokens = {'root': []}
+ try:
+ list(lex('dummy', BuggyLexer()))
+ except TypeError as e:
+ assert 'lex() argument must be a lexer instance' not in str(e)
+ else:
+ assert False, 'no error raised by buggy lexer?'
+
+ class BuggyFormatter(Formatter):
+ def format(self, tokensource, outfile, extra_argument):
+ pass
+ try:
+ format([], BuggyFormatter())
+ except TypeError as e:
+ assert 'format() argument must be a formatter instance' not in str(e)
+ else:
+ assert False, 'no error raised by buggy formatter?'
+
+class TestFilters:
+
+ def test_basic(self):
+ filters_args = [
+ ('whitespace', {'spaces': True, 'tabs': True, 'newlines': True}),
+ ('whitespace', {'wstokentype': False, 'spaces': True}),
+ ('highlight', {'names': ['isinstance', 'lexers', 'x']}),
+ ('codetagify', {'codetags': 'API'}),
+ ('keywordcase', {'case': 'capitalize'}),
+ ('raiseonerror', {}),
+ ('gobble', {'n': 4}),
+ ('tokenmerge', {}),
+ ('symbols', {'lang': 'isabelle'}),
+ ]
+ for x, args in filters_args:
+ lx = lexers.PythonLexer()
+ lx.add_filter(x, **args)
+ # We don't read as binary and decode, but instead read as text, as
+ # we need consistent line endings. Otherwise we'll get \r\n on
+ # Windows
+ with open(TESTFILE, encoding='utf-8') as fp:
+ text = fp.read()
+ tokens = list(lx.get_tokens(text))
+ assert all(isinstance(t[1], str) for t in tokens), \
+ '%s filter did not return Unicode' % x
+ roundtext = ''.join([t[1] for t in tokens])
+ if x not in ('whitespace', 'keywordcase', 'gobble'):
+ # these filters change the text
+ assert roundtext == text, \
+ "lexer roundtrip with %s filter failed" % x
+
+ def test_raiseonerror(self):
+ lx = lexers.PythonLexer()
+ lx.add_filter('raiseonerror', excclass=RuntimeError)
+ assert pytest.raises(RuntimeError, list, lx.get_tokens('$'))
+
+ def test_whitespace(self):
+ lx = lexers.PythonLexer()
+ lx.add_filter('whitespace', spaces='%')
+ with open(TESTFILE, 'rb') as fp:
+ text = fp.read().decode('utf-8')
+ lxtext = ''.join([t[1] for t in list(lx.get_tokens(text))])
+ assert ' ' not in lxtext
+
+ def test_keywordcase(self):
+ lx = lexers.PythonLexer()
+ lx.add_filter('keywordcase', case='capitalize')
+ with open(TESTFILE, 'rb') as fp:
+ text = fp.read().decode('utf-8')
+ lxtext = ''.join([t[1] for t in list(lx.get_tokens(text))])
+ assert 'Def' in lxtext and 'Class' in lxtext
+
+ def test_codetag(self):
+ lx = lexers.PythonLexer()
+ lx.add_filter('codetagify')
+ text = '# BUG: text'
+ tokens = list(lx.get_tokens(text))
+ assert '# ' == tokens[0][1]
+ assert 'BUG' == tokens[1][1]
+
+ def test_codetag_boundary(self):
+ # ticket #368
+ lx = lexers.PythonLexer()
+ lx.add_filter('codetagify')
+ text = '# DEBUG: text'
+ tokens = list(lx.get_tokens(text))
+ assert '# DEBUG: text' == tokens[0][1]
+
+ def test_symbols(self):
+ lx = lexers.IsabelleLexer()
+ lx.add_filter('symbols')
+ text = 'lemma "A \\<Longrightarrow> B"'
+ tokens = list(lx.get_tokens(text))
+ assert 'lemma' == tokens[0][1]
+ assert 'A ' == tokens[3][1]
+ assert '\U000027f9' == tokens[4][1]
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
new file mode 100644
index 0000000..c05fd01
--- /dev/null
+++ b/tests/test_cmdline.py
@@ -0,0 +1,324 @@
+"""
+ Command line test
+ ~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import io
+import os
+import re
+import sys
+import tempfile
+from io import BytesIO
+from os import path
+
+import pytest
+from pytest import raises
+
+from pygments import cmdline, highlight
+
+TESTDIR = path.dirname(path.abspath(__file__))
+TESTFILE = path.join(TESTDIR, 'test_cmdline.py')
+
+TESTCODE = '''\
+def func(args):
+ pass
+'''
+
+
+def _decode_output(text):
+ try:
+ return text.decode('utf-8')
+ except UnicodeEncodeError: # implicit encode on Python 2 with data loss
+ return text
+
+
+def run_cmdline(*args, **kwds):
+ saved_stdin = sys.stdin
+ saved_stdout = sys.stdout
+ saved_stderr = sys.stderr
+ stdin_buffer = BytesIO()
+ stdout_buffer = BytesIO()
+ stderr_buffer = BytesIO()
+ new_stdin = sys.stdin = io.TextIOWrapper(stdin_buffer, 'utf-8')
+ new_stdout = sys.stdout = io.TextIOWrapper(stdout_buffer, 'utf-8')
+ new_stderr = sys.stderr = io.TextIOWrapper(stderr_buffer, 'utf-8')
+ new_stdin.write(kwds.get('stdin', ''))
+ new_stdin.seek(0, 0)
+ try:
+ ret = cmdline.main(['pygmentize'] + list(args))
+ finally:
+ sys.stdin = saved_stdin
+ sys.stdout = saved_stdout
+ sys.stderr = saved_stderr
+ new_stdout.flush()
+ new_stderr.flush()
+ out, err = stdout_buffer.getvalue(), \
+ stderr_buffer.getvalue()
+ return (ret, _decode_output(out), _decode_output(err))
+
+
+def check_success(*cmdline, **kwds):
+ code, out, err = run_cmdline(*cmdline, **kwds)
+ assert code == 0
+ assert err == ''
+ return out
+
+
+def check_failure(*cmdline, **kwds):
+ expected_code = kwds.pop('code', 1)
+ try:
+ code, out, err = run_cmdline(*cmdline, **kwds)
+ except SystemExit as err:
+ assert err.args[0] == expected_code
+ else:
+ assert code == expected_code
+ assert out == ''
+ return err
+
+
+def test_normal():
+ # test that cmdline gives the same output as library api
+ from pygments.lexers import PythonLexer
+ from pygments.formatters import HtmlFormatter
+ filename = TESTFILE
+ with open(filename, 'rb') as fp:
+ code = fp.read()
+
+ output = highlight(code, PythonLexer(), HtmlFormatter())
+
+ o = check_success('-lpython', '-fhtml', filename)
+ assert o == output
+
+
+def test_stdin():
+ o = check_success('-lpython', '-fhtml', stdin=TESTCODE)
+ o = re.sub('<[^>]*>', '', o)
+ # rstrip is necessary since HTML inserts a \n after the last </div>
+ assert o.rstrip() == TESTCODE.rstrip()
+
+ # guess if no lexer given
+ o = check_success('-fhtml', stdin=TESTCODE)
+ o = re.sub('<[^>]*>', '', o)
+ # rstrip is necessary since HTML inserts a \n after the last </div>
+ assert o.rstrip() == TESTCODE.rstrip()
+
+
+def test_outfile():
+ # test that output file works with and without encoding
+ fd, name = tempfile.mkstemp()
+ os.close(fd)
+ for opts in [['-fhtml', '-o', name, TESTFILE],
+ ['-flatex', '-o', name, TESTFILE],
+ ['-fhtml', '-o', name, '-O', 'encoding=utf-8', TESTFILE]]:
+ try:
+ check_success(*opts)
+ finally:
+ os.unlink(name)
+
+
+def test_load_from_file():
+ lexer_file = os.path.join(TESTDIR, 'support', 'python_lexer.py')
+ formatter_file = os.path.join(TESTDIR, 'support', 'html_formatter.py')
+
+ # By default, use CustomLexer
+ o = check_success('-l', lexer_file, '-f', 'html', '-x', stdin=TESTCODE)
+ o = re.sub('<[^>]*>', '', o)
+ # rstrip is necessary since HTML inserts a \n after the last </div>
+ assert o.rstrip() == TESTCODE.rstrip()
+
+ # If user specifies a name, use it
+ o = check_success('-f', 'html', '-x', '-l',
+ lexer_file + ':LexerWrapper', stdin=TESTCODE)
+ o = re.sub('<[^>]*>', '', o)
+ # rstrip is necessary since HTML inserts a \n after the last </div>
+ assert o.rstrip() == TESTCODE.rstrip()
+
+ # Should also work for formatters
+ o = check_success('-lpython', '-f',
+ formatter_file + ':HtmlFormatterWrapper',
+ '-x', stdin=TESTCODE)
+ o = re.sub('<[^>]*>', '', o)
+ # rstrip is necessary since HTML inserts a \n after the last </div>
+ assert o.rstrip() == TESTCODE.rstrip()
+
+
+def test_stream_opt():
+ o = check_success('-lpython', '-s', '-fterminal', stdin=TESTCODE)
+ o = re.sub(r'\x1b\[.*?m', '', o)
+ assert o.replace('\r\n', '\n') == TESTCODE
+
+
+def test_h_opt():
+ o = check_success('-h')
+ assert 'usage:' in o
+
+
+def test_L_opt():
+ o = check_success('-L')
+ assert 'Lexers' in o and 'Formatters' in o and 'Filters' in o and 'Styles' in o
+ o = check_success('-L', 'lexer')
+ assert 'Lexers' in o and 'Formatters' not in o
+ check_success('-L', 'lexers')
+
+
+def test_O_opt():
+ filename = TESTFILE
+ o = check_success('-Ofull=1,linenos=true,foo=bar', '-fhtml', filename)
+ assert '<html' in o
+ assert 'class="linenos"' in o
+
+ # "foobar" is invalid for a bool option
+ e = check_failure('-Ostripnl=foobar', TESTFILE)
+ assert 'Error: Invalid value' in e
+ e = check_failure('-Ostripnl=foobar', '-lpy')
+ assert 'Error: Invalid value' in e
+
+
+def test_P_opt():
+ filename = TESTFILE
+ o = check_success('-Pfull', '-Ptitle=foo, bar=baz=,', '-fhtml', filename)
+ assert '<title>foo, bar=baz=,</title>' in o
+
+
+def test_F_opt():
+ filename = TESTFILE
+ o = check_success('-Fhighlight:tokentype=Name.Blubb,'
+ 'names=TESTFILE filename', '-fhtml', filename)
+ assert '<span class="n n-Blubb' in o
+
+
+def test_H_opt():
+ o = check_success('-H', 'formatter', 'html')
+ assert 'HTML' in o
+ o = check_success('-H', 'lexer', 'python')
+ assert 'Python' in o
+ o = check_success('-H', 'filter', 'raiseonerror')
+ assert 'raiseonerror' in o
+ e = check_failure('-H', 'lexer', 'foobar')
+ assert 'not found' in e
+
+
+def test_S_opt():
+ o = check_success('-S', 'default', '-f', 'html', '-O', 'linenos=1')
+ lines = o.splitlines()
+ for line in lines[5:]:
+ # every line is for a token class, except for the first 5 lines,
+ # which define styles for `pre` and line numbers
+ parts = line.split()
+ assert parts[0].startswith('.')
+ assert parts[1] == '{'
+ if parts[0] != '.hll':
+ assert parts[-4] == '}'
+ assert parts[-3] == '/*'
+ assert parts[-1] == '*/'
+ check_failure('-S', 'default', '-f', 'foobar')
+
+
+def test_N_opt():
+ o = check_success('-N', 'test.py')
+ assert 'python' == o.strip()
+ o = check_success('-N', 'test.unknown')
+ assert 'text' == o.strip()
+
+
+def test_C_opt():
+ o = check_success('-C', stdin='#!python3\n')
+ assert 'python' == o.strip()
+ o = check_success('-C', stdin='x')
+ assert 'text' == o.strip()
+
+
+@pytest.mark.parametrize('opts', [
+ ('-X',),
+ ('-L', '-lpy'),
+ ('-L', '-fhtml'),
+ ('-L', '-Ox'),
+ ('-S', 'default', '-l', 'py', '-f', 'html'),
+ ('-S', 'default'),
+ ('-a', 'arg'),
+ ('-H',),
+ (TESTFILE, TESTFILE),
+ ('-H', 'formatter'),
+ ('-H', 'foo', 'bar'),
+ ('-s',),
+ ('-s', TESTFILE),
+])
+def test_invalid_opts(opts):
+ check_failure(*opts, code=2)
+
+
+def test_errors():
+ # input file not found
+ e = check_failure('-lpython', 'nonexistent.py')
+ assert 'Error: cannot read infile' in e
+ assert 'nonexistent.py' in e
+
+ # lexer not found
+ e = check_failure('-lfooo', TESTFILE)
+ assert 'Error: no lexer for alias' in e
+
+ # cannot load .py file without load_from_file flag
+ e = check_failure('-l', 'nonexistent.py', TESTFILE)
+ assert 'Error: no lexer for alias' in e
+
+ # lexer file is missing/unreadable
+ e = check_failure('-l', 'nonexistent.py', '-x', TESTFILE)
+ assert 'Error: cannot read' in e
+
+ # lexer file is malformed
+ e = check_failure('-l', path.join(TESTDIR, 'support', 'empty.py'),
+ '-x', TESTFILE)
+ assert 'Error: no valid CustomLexer class found' in e
+
+ # formatter not found
+ e = check_failure('-lpython', '-ffoo', TESTFILE)
+ assert 'Error: no formatter found for name' in e
+
+ # formatter for outfile not found
+ e = check_failure('-ofoo.foo', TESTFILE)
+ assert 'Error: no formatter found for file name' in e
+
+ # cannot load .py file without load_from_file flag
+ e = check_failure('-f', 'nonexistent.py', TESTFILE)
+ assert 'Error: no formatter found for name' in e
+
+ # formatter file is missing/unreadable
+ e = check_failure('-f', 'nonexistent.py', '-x', TESTFILE)
+ assert 'Error: cannot read' in e
+
+ # formatter file is malformed
+ e = check_failure('-f', path.join(TESTDIR, 'support', 'empty.py'),
+ '-x', TESTFILE)
+ assert 'Error: no valid CustomFormatter class found' in e
+
+ # output file not writable
+ e = check_failure('-o', os.path.join('nonexistent', 'dir', 'out.html'),
+ '-lpython', TESTFILE)
+ assert 'Error: cannot open outfile' in e
+ assert 'out.html' in e
+
+ # unknown filter
+ e = check_failure('-F', 'foo', TESTFILE)
+ assert 'Error: filter \'foo\' not found' in e
+
+
+def test_exception():
+ cmdline.highlight = None # override callable to provoke TypeError
+ try:
+ # unexpected exception while highlighting
+ e = check_failure('-lpython', TESTFILE)
+ assert '*** Error while highlighting:' in e
+ assert 'TypeError' in e
+
+ # same with -v: should reraise the exception
+ assert raises(Exception, check_failure, '-lpython', '-v', TESTFILE)
+ finally:
+ cmdline.highlight = highlight
+
+
+def test_parse_opts():
+ assert cmdline._parse_options([' ', 'keyonly,key = value ']) == \
+ {'keyonly': True, 'key': 'value'}
diff --git a/tests/test_coffeescript.py b/tests/test_coffeescript.py
new file mode 100644
index 0000000..3d16727
--- /dev/null
+++ b/tests/test_coffeescript.py
@@ -0,0 +1,52 @@
+"""
+ CoffeeScript tests
+ ~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers import CoffeeScriptLexer
+from pygments.token import Token
+
+COFFEE_SLASH_GOLDEN = [
+ # input_str, slashes_are_regex_here
+ (r'/\\/', True),
+ (r'/\\/i', True),
+ (r'/\//', True),
+ (r'/(\s)/', True),
+ ('/a{2,8}/', True),
+ ('/b*c?d+/', True),
+ ('/(capture-match)/', True),
+ ('/(?:do-not-capture-match)/', True),
+ ('/this|or|that/', True),
+ ('/[char-set]/', True),
+ ('/[^neg-char_st]/', True),
+ ('/^.*$/', True),
+ (r'/\n(\f)\0\1\d\b\cm\u1234/', True),
+ (r'/^.?([^/\\\n\w]*)a\1+$/.something(or_other) # something more complex', True),
+ ("foo = (str) ->\n /'|\"/.test str", True),
+ ('a = a / b / c', False),
+ ('a = a/b/c', False),
+ ('a = a/b/ c', False),
+ ('a = a /b/c', False),
+ ('a = 1 + /d/.test(a)', True),
+]
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield CoffeeScriptLexer()
+
+
+@pytest.mark.parametrize('golden', COFFEE_SLASH_GOLDEN)
+def test_coffee_slashes(lexer, golden):
+ input_str, slashes_are_regex_here = golden
+ output = list(lexer.get_tokens(input_str))
+ print(output)
+ for t, s in output:
+ if '/' in s:
+ is_regex = t is Token.String.Regex
+ assert is_regex == slashes_are_regex_here, (t, s)
diff --git a/tests/test_crystal.py b/tests/test_crystal.py
new file mode 100644
index 0000000..962d9e5
--- /dev/null
+++ b/tests/test_crystal.py
@@ -0,0 +1,80 @@
+"""
+ Basic CrystalLexer Test
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.token import Text, String, Number, Punctuation, Error, Whitespace
+from pygments.lexers import CrystalLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield CrystalLexer()
+
+
+def test_numbers(lexer):
+ for kind, testset in [
+ (Number.Integer, '0 1 1_000_000 1u8 11231231231121312i64'),
+ (Number.Float, '0.0 1.0_f32 1_f32 0f64 1e+4 1e111 1_234.567_890'),
+ (Number.Bin, '0b1001_0110 0b0u8'),
+ (Number.Oct, '0o17 0o7_i32'),
+ (Number.Hex, '0xdeadBEEF'),
+ ]:
+ for fragment in testset.split():
+ assert list(lexer.get_tokens(fragment + '\n')) == \
+ [(kind, fragment), (Whitespace, '\n')]
+
+ for fragment in '01 0b2 0x129g2 0o12358'.split():
+ assert next(lexer.get_tokens(fragment + '\n'))[0] == Error
+
+
+def test_symbols(lexer):
+ for fragment in [':sym_bol', ':\u3042', ':question?']:
+ assert list(lexer.get_tokens(fragment + '\n')) == \
+ [(String.Symbol, fragment), (Whitespace, '\n')]
+
+ fragment = ':"sym bol"\n'
+ tokens = [
+ (String.Symbol, ':"'),
+ (String.Symbol, 'sym bol'),
+ (String.Symbol, '"'),
+ (Whitespace, '\n'),
+ ]
+ assert list(lexer.get_tokens(fragment)) == tokens
+
+
+def test_chars(lexer):
+ for fragment in ["'a'", "'я'", "'\\u{1234}'", "'\n'"]:
+ assert list(lexer.get_tokens(fragment + '\n')) == \
+ [(String.Char, fragment), (Whitespace, '\n')]
+ assert next(lexer.get_tokens("'abc'"))[0] == Error
+
+
+def test_string_escapes(lexer):
+ for body in ['\\n', '\\a', '\\xff', '\\u1234', '\\000', '\\u{0}', '\\u{10AfF9}']:
+ fragment = '"a' + body + 'z"\n'
+ assert list(lexer.get_tokens(fragment)) == [
+ (String.Double, '"'),
+ (String.Double, 'a'),
+ (String.Escape, body),
+ (String.Double, 'z'),
+ (String.Double, '"'),
+ (Whitespace, '\n'),
+ ]
+
+
+def test_empty_percent_strings(lexer):
+ for body in ['%()', '%[]', '%{}', '%<>', '%||']:
+ fragment = '(' + body + ')\n'
+ assert list(lexer.get_tokens(fragment)) == [
+ (Punctuation, '('),
+ (String.Other, body[:-1]),
+ (String.Other, body[-1]),
+ (Punctuation, ')'),
+ (Whitespace, '\n'),
+ ]
diff --git a/tests/test_data.py b/tests/test_data.py
new file mode 100644
index 0000000..719f8bf
--- /dev/null
+++ b/tests/test_data.py
@@ -0,0 +1,285 @@
+"""
+ Data Tests
+ ~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import time
+
+import pytest
+
+from pygments.lexers.data import JsonLexer, JsonBareObjectLexer, JsonLdLexer
+from pygments.token import Comment, Error, Token, Punctuation, Number, String, \
+ Keyword, Name, Whitespace
+
+
+@pytest.fixture(scope='module')
+def lexer_json():
+ yield JsonLexer()
+
+
+@pytest.fixture(scope='module')
+def lexer_bare():
+ yield JsonBareObjectLexer()
+
+
+@pytest.fixture(scope='module')
+def lexer_json_ld():
+ yield JsonLdLexer()
+
+
+@pytest.mark.parametrize(
+ 'text, expected_token_types',
+ (
+ # Integers
+ ('0', (Number.Integer,)),
+ ('-1', (Number.Integer,)),
+ ('1234567890', (Number.Integer,)),
+ ('-1234567890', (Number.Integer,)),
+
+ # Floats, including scientific notation
+ ('123456789.0123456789', (Number.Float,)),
+ ('-123456789.0123456789', (Number.Float,)),
+ ('1e10', (Number.Float,)),
+ ('-1E10', (Number.Float,)),
+ ('1e-10', (Number.Float,)),
+ ('-1E+10', (Number.Float,)),
+ ('1.0e10', (Number.Float,)),
+ ('-1.0E10', (Number.Float,)),
+ ('1.0e-10', (Number.Float,)),
+ ('-1.0E+10', (Number.Float,)),
+
+ # Strings (escapes are tested elsewhere)
+ ('""', (String.Double,)),
+ ('"abc"', (String.Double,)),
+ ('"ひらがな"', (String.Double,)),
+ ('"123"', (String.Double,)),
+ ('"[]"', (String.Double,)),
+ ('"{}"', (String.Double,)),
+ ('"true"', (String.Double,)),
+ ('"false"', (String.Double,)),
+ ('"null"', (String.Double,)),
+ ('":,"', (String.Double,)),
+
+ # Constants
+ ('true', (Keyword.Constant, )),
+ ('false', (Keyword.Constant, )),
+ ('null', (Keyword.Constant, )),
+
+ # Arrays
+ ('[]', (Punctuation,)),
+ ('["a", "b"]', (Punctuation, String.Double, Punctuation,
+ Whitespace, String.Double, Punctuation)),
+
+ # Objects
+ ('{}', (Punctuation,)),
+ ('{"a": "b"}', (Punctuation, Name.Tag, Punctuation,
+ Whitespace, String.Double, Punctuation)),
+ )
+)
+@pytest.mark.parametrize('end', ('', '\n'))
+def test_json_literals_positive_match(lexer_json, text, expected_token_types, end):
+ """Validate that syntactically-correct JSON literals are parsed correctly."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed(text + end))
+ assert len(tokens) == len(expected_token_types) + bool(end)
+ assert all(token[1] is expected_token
+ for token, expected_token in zip(tokens, expected_token_types))
+ assert ''.join(token[2] for token in tokens) == text + end
+
+
+@pytest.mark.parametrize(
+ 'text, expected',
+ (
+ ('\u0020', Whitespace), # space
+ ('\u000a', Whitespace), # newline
+ ('\u000d', Whitespace), # carriage return
+ ('\u0009', Whitespace), # tab
+ )
+)
+def test_json_whitespace_positive_match(lexer_json, text, expected):
+ """Validate that whitespace is parsed correctly."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert tokens == [(0, expected, text)]
+
+ # Expand the whitespace and verify parsing again.
+ tokens = list(lexer_json.get_tokens_unprocessed(text * 2 + ' '))
+ assert tokens == [(0, expected, text * 2 + ' ')]
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '"', '\\', '/', 'b', 'f', 'n', 'r', 't',
+ 'u0123', 'u4567', 'u89ab', 'ucdef', 'uABCD', 'uEF01',
+ )
+)
+def test_json_object_key_escapes_positive_match(lexer_json, text):
+ """Validate that escape sequences in JSON object keys are parsed correctly."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed('{"\\%s": 1}' % text))
+ assert len(tokens) == 6
+ assert tokens[1][1] is Name.Tag
+ assert tokens[1][2] == '"\\%s"' % text
+
+
+@pytest.mark.parametrize('text', ('uz', 'u1z', 'u12z', 'u123z'))
+def test_json_string_unicode_escapes_negative_match(lexer_json, text):
+ """Validate that if unicode escape sequences end abruptly there's no problem."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed('"\\%s"' % text))
+ assert len(tokens) == 1
+ assert tokens[0] == (0, String.Double, '"\\%s"' % text)
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '"', '\\', '/', 'b', 'f', 'n', 'r', 't',
+ 'u0123', 'u4567', 'u89ab', 'ucdef', 'uABCD', 'uEF01',
+ )
+)
+def test_json_string_escapes_positive_match(lexer_json, text):
+ """Validate that escape sequences in JSON string values are parsed correctly."""
+
+ text = '"\\%s"' % text
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert len(tokens) == 1
+ assert tokens[0][1] is String.Double
+ assert tokens[0][2] == text
+
+
+@pytest.mark.parametrize('text', ('+\n', '0\n', '""0\n', 'a\nb\n', '""/-'))
+def test_json_round_trip_errors(lexer_json, text):
+ """Validate that past round-trip errors never crop up again."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert ''.join(t[2] for t in tokens) == text
+
+
+def test_json_comments_single_line_positive_matches(lexer_json):
+ """Verify that single-line comments are tokenized correctly."""
+
+ text = '{"a//b"//C1\n:123/////C2\n}\n// // C3'
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert tokens[2] == (7, Comment.Single, "//C1")
+ assert tokens[6] == (16, Comment.Single, "/////C2")
+ assert tokens[10] == (26, Comment.Single, "// // C3")
+
+ comment_count = sum(1 for token in tokens if token[1] == Comment or token[1].parent == Comment)
+ assert comment_count == 3
+
+ parsed_text = ''.join(token[2] for token in tokens)
+ assert parsed_text == text, 'Input and output texts must match!'
+
+
+def test_json_comments_multiline_positive_matches(lexer_json):
+ """Verify that multiline comments are tokenized correctly."""
+
+ text = '/** / **/{"a /**/ b"/* \n */:123}'
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert tokens[0] == (0, Comment.Multiline, "/** / **/")
+ assert tokens[3] == (20, Comment.Multiline, "/* \n */")
+
+ comment_count = sum(1 for token in tokens if token[1] == Comment or token[1].parent == Comment)
+ assert comment_count == 2
+
+ parsed_text = ''.join(token[2] for token in tokens)
+ assert parsed_text == text, 'Input and output texts must match!'
+
+
+@pytest.mark.parametrize(
+ "text, expected",
+ (
+ # Unfinished comment openers
+ ('/', (0, Error, '/')),
+ ('1/', (1, Error, '/')),
+ ('/1', (0, Error, '/')),
+ ('""/', (2, Error, '/')),
+ # Unclosed multiline comments
+ ('/*', (0, Error, '/*')),
+ ('/**', (0, Error, '/**')),
+ ('/*/', (0, Error, '/*/')),
+ ('1/*', (1, Error, '/*')),
+ ('""/*', (2, Error, '/*')),
+ ('""/**', (2, Error, '/**')),
+ )
+)
+def test_json_comments_negative_matches(lexer_json, text, expected):
+ """Verify that the unfinished or unclosed comments are parsed as errors."""
+
+ tokens = list(lexer_json.get_tokens_unprocessed(text))
+ assert expected in tokens
+
+ parsed_text = ''.join(token[2] for token in tokens)
+ assert parsed_text == text, 'Input and output texts must match!'
+
+
+def test_json_escape_backtracking(lexer_json):
+ """Confirm that there is no catastrophic backtracking in the lexer.
+
+ This no longer applies because the JSON lexer doesn't use regular expressions,
+ but the test is included to ensure no loss of functionality now or in the future.
+ """
+
+ fragment = r'{"\u00D0000\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\63CD'
+ start_time = time.time()
+ list(lexer_json.get_tokens(fragment))
+ assert time.time() - start_time < 1, 'The JSON lexer may have catastrophic backtracking'
+
+
+@pytest.mark.parametrize(
+ 'keyword',
+ (
+ 'base',
+ 'container',
+ 'context',
+ 'direction',
+ 'graph',
+ 'id',
+ 'import',
+ 'included',
+ 'index',
+ 'json',
+ 'language',
+ 'list',
+ 'nest',
+ 'none',
+ 'prefix',
+ 'propagate',
+ 'protected',
+ 'reverse',
+ 'set',
+ 'type',
+ 'value',
+ 'version',
+ 'vocab',
+ )
+)
+def test_json_ld_keywords_positive_match(lexer_json_ld, keyword):
+ """Validate that JSON-LD keywords are parsed correctly."""
+
+ tokens = list(lexer_json_ld.get_tokens_unprocessed('{"@%s": ""}' % keyword))
+ assert len(tokens) == 6
+ assert tokens[1][1] is Token.Name.Decorator
+ assert tokens[1][2] == '"@%s"' % keyword
+
+
+@pytest.mark.parametrize(
+ 'keyword',
+ (
+ '@bogus', # "@" does not guarantee a keyword match
+ '@bases', # Begins with the keyword "@base"
+ 'container', # Matches "container" but has no leading "@"
+ )
+)
+def test_json_ld_keywords_negative_match(lexer_json_ld, keyword):
+ """Validate that JSON-LD non-keywords are parsed correctly."""
+
+ tokens = list(lexer_json_ld.get_tokens_unprocessed('{"%s": ""}' % keyword))
+ assert len(tokens) == 6
+ assert tokens[1][1] is Token.Name.Tag
+ assert tokens[1][2] == '"%s"' % keyword
diff --git a/tests/test_devicetree_lexer.py b/tests/test_devicetree_lexer.py
new file mode 100644
index 0000000..e3aeb4d
--- /dev/null
+++ b/tests/test_devicetree_lexer.py
@@ -0,0 +1,32 @@
+"""
+ Devicetree Lexer Tests
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.devicetree import DevicetreeLexer
+from pygments.token import Token
+
+
+@pytest.fixture(scope="module")
+def lexer():
+ yield DevicetreeLexer()
+
+
+@pytest.mark.parametrize(
+ "fragment",
+ (
+ 'nodelabel: node@0 { foo = "bar"; };',
+ 'nodelabel: node { foo = "bar"; };',
+ 'nodelabel0: nodelabel1: node@0 { foo = "bar"; };',
+ ),
+)
+def test_fragment_out_of_root_node(lexer, fragment):
+ """Validate that a devicetree fragment out of a root node is parsed correctly."""
+
+ tokens = list(lexer.get_tokens(fragment))
+ assert all(x[0] != Token.Error for x in tokens)
diff --git a/tests/test_func.py b/tests/test_func.py
new file mode 100644
index 0000000..c494b9c
--- /dev/null
+++ b/tests/test_func.py
@@ -0,0 +1,44 @@
+import pytest
+from pygments.lexers.func import FuncLexer
+from pygments.token import Token, Name
+
+@pytest.fixture(scope='module')
+def lexer_func():
+ yield FuncLexer()
+
+
+@pytest.mark.parametrize('text', (
+ 'take(first)Entry', '"not_a_string', 'msg.sender', 'send_message,then_terminate', '_'))
+def test_func_not_identifier(lexer_func, text):
+ """Test text that should **not** be tokenized as identifier."""
+ assert list(lexer_func.get_tokens(text))[0] != (Name.Variable, text)
+
+
+@pytest.mark.parametrize('text', (
+ '`test identifier`', 'simple_identifier', 'query\'\'',
+ '_internal_value', 'get_pubkeys&signatures',
+ 'dict::udict_set_builder', '2+2=2*2', '-alsovalidname', '{hehehe}'))
+def test_func_identifier(lexer_func, text):
+ """Test text that should be tokenized as identifier."""
+ assert list(lexer_func.get_tokens(text))[0] == (Name.Variable, text)
+
+
+@pytest.mark.parametrize('text', (
+'`test identifier`(', 'simple_identifier(', 'query\'\'(',
+'_internal_value(', 'get_pubkeys&signatures(',
+'dict::udict_set_builder(', '2+2=2*2(', '-alsovalidname(', '{hehehe}('))
+def test_func_function(lexer_func, text):
+ """Test text that should be tokenized as identifier."""
+ assert list(lexer_func.get_tokens(text))[0] == (Name.Function, text[:-1])
+
+
+@pytest.mark.parametrize('text', ('0x0f', '0x1_2', '123', '0b10', '0xffff_fff', '1'))
+def test_func_number(lexer_func, text):
+ """Test text that should be tokenized as number."""
+ assert list(lexer_func.get_tokens(text))[0] == (Token.Literal.Number, text)
+
+
+@pytest.mark.parametrize('text', ('0x0f_m', '0X1_2', '12d3', '0b1_0f', '0bff_fff', '0b'))
+def test_func_not_number(lexer_func, text):
+ """Test text that should *not* be tokenized as number."""
+ assert list(lexer_func.get_tokens(text))[0] != (Token.Literal.Number, text)
diff --git a/tests/test_groff_formatter.py b/tests/test_groff_formatter.py
new file mode 100644
index 0000000..faad338
--- /dev/null
+++ b/tests/test_groff_formatter.py
@@ -0,0 +1,40 @@
+"""
+ Pygments Groff formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pygments import highlight
+from pygments.lexer import RegexLexer
+from pygments.style import Style
+from pygments.token import Token
+from pygments.formatters import GroffFormatter
+
+
+# FIXME: this tests a bug fix, but the basic functionality
+# is not tested thoroughly yet.
+
+class ToyLexer(RegexLexer):
+ tokens = {
+ "root": [
+ ("a", Token.Name),
+ ("b", Token.Name.Custom),
+ ],
+ }
+
+class ToyStyle(Style):
+ styles = {
+ Token.Name: "bold",
+ }
+
+
+expected = r""".nf
+\f[CR]
+\f[CB]a\f[CR]\f[CB]b\f[CR]
+
+.fi"""
+
+def test_inheritance_custom_tokens():
+ assert highlight("ab", ToyLexer(), GroffFormatter(style=ToyStyle)) == expected
diff --git a/tests/test_guess.py b/tests/test_guess.py
new file mode 100644
index 0000000..8adf161
--- /dev/null
+++ b/tests/test_guess.py
@@ -0,0 +1,184 @@
+"""
+ Pygments basic API tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pathlib import Path
+
+import pytest
+
+from pygments.lexers import guess_lexer, get_lexer_by_name
+from pygments.lexers.basic import CbmBasicV2Lexer
+from pygments.lexers.ecl import ECLLexer
+
+TESTDIR = Path(__file__).resolve().parent
+
+
+def get_input(lexer, filename):
+ return Path(TESTDIR, 'examplefiles', lexer, filename).read_text(encoding='utf-8')
+
+
+@pytest.mark.skip(reason="This is identified as T-SQL")
+def test_guess_lexer_fsharp():
+ lx = guess_lexer(get_input('fsharp', 'Deflate.fs'))
+ assert lx.__class__.__name__ == 'FSharpLexer'
+
+
+def test_guess_lexer_brainfuck():
+ lx = guess_lexer('>>[-]<<[->>+<<]')
+ assert lx.__class__.__name__ == 'BrainfuckLexer'
+
+
+def test_guess_lexer_singularity():
+ lx = guess_lexer(get_input('singularity', 'Singularity'))
+ assert lx.__class__.__name__ == 'SingularityLexer'
+
+
+@pytest.mark.skip(reason="This is identified as MIME")
+def test_guess_lexer_matlab():
+ lx = guess_lexer(r'A \ B')
+ assert lx.__class__.__name__ == 'OctaveLexer'
+
+
+@pytest.mark.skip(reason="This is identified as Python")
+def test_guess_lexer_hybris():
+ lx = guess_lexer(get_input('hybris', 'hybris_File.hy'))
+ assert lx.__class__.__name__ == 'HybrisLexer'
+
+
+def test_guess_lexer_forth():
+ lx = guess_lexer(get_input('forth', 'demo.frt'))
+ assert lx.__class__.__name__ == 'ForthLexer'
+
+
+def test_guess_lexer_modula2():
+ lx = guess_lexer(get_input('modula2', 'modula2_test_cases.def'))
+ assert lx.__class__.__name__ == 'Modula2Lexer'
+
+
+def test_guess_lexer_unicon():
+ lx = guess_lexer(get_input('unicon', 'example.icn'))
+ assert lx.__class__.__name__ == 'UcodeLexer'
+
+
+def test_guess_lexer_ezhil():
+ lx = guess_lexer(get_input('ezhil', 'ezhil_primefactors.n'))
+ assert lx.__class__.__name__ == 'EzhilLexer'
+
+
+def test_guess_lexer_gdscript():
+ lx = guess_lexer(get_input('gdscript', 'gdscript_example.gd'))
+ assert lx.__class__.__name__ == 'GDScriptLexer'
+
+
+def test_guess_lexer_gap():
+ lx = guess_lexer(get_input('gap', 'example.gd'))
+ assert lx.__class__.__name__ == 'GAPLexer'
+ lx = guess_lexer(get_input('gap', 'example.gi'))
+ assert lx.__class__.__name__ == 'GAPLexer'
+
+
+def test_guess_lexer_easytrieve():
+ lx = guess_lexer(get_input('easytrieve', 'example.ezt'))
+ assert lx.__class__.__name__ == 'EasytrieveLexer'
+ lx = guess_lexer(get_input('easytrieve', 'example.mac'))
+ assert lx.__class__.__name__ == 'EasytrieveLexer'
+
+
+def test_guess_lexer_jcl():
+ lx = guess_lexer(get_input('jcl', 'example.jcl'))
+ assert lx.__class__.__name__ == 'JclLexer'
+
+
+def test_guess_lexer_rexx():
+ lx = guess_lexer(get_input('rexx', 'example.rexx'))
+ assert lx.__class__.__name__ == 'RexxLexer'
+
+
+def test_easytrieve_can_guess_from_text():
+ lx = get_lexer_by_name('easytrieve')
+ assert lx.analyse_text('MACRO')
+ assert lx.analyse_text('\nMACRO')
+ assert lx.analyse_text(' \nMACRO')
+ assert lx.analyse_text(' \n MACRO')
+ assert lx.analyse_text('*\nMACRO')
+ assert lx.analyse_text('*\n *\n\n \n*\n MACRO')
+
+
+def test_rexx_can_guess_from_text():
+ lx = get_lexer_by_name('rexx')
+ assert lx.analyse_text('/* */') == pytest.approx(0.01)
+ assert lx.analyse_text('''/* Rexx */
+ say "hello world"''') == pytest.approx(1.0)
+ val = lx.analyse_text('/* */\n'
+ 'hello:pRoceduRe\n'
+ ' say "hello world"')
+ assert val > 0.5
+ val = lx.analyse_text('''/* */
+ if 1 > 0 then do
+ say "ok"
+ end
+ else do
+ say "huh?"
+ end''')
+ assert val > 0.2
+ val = lx.analyse_text('''/* */
+ greeting = "hello world!"
+ parse value greeting "hello" name "!"
+ say name''')
+ assert val > 0.2
+
+
+def test_guess_cmake_lexer_from_header():
+ headers = [
+ "CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR)",
+ "cmake_minimum_required(version 3.13) # CMake version check",
+ " CMAKE_MINIMUM_REQUIRED\t( VERSION 2.6 FATAL_ERROR ) ",
+ ]
+ for header in headers:
+ code = '\n'.join([
+ header,
+ 'project(example)',
+ 'set(CMAKE_CXX_STANDARD 14)',
+ 'set(SOURCE_FILES main.cpp)',
+ 'add_executable(example ${SOURCE_FILES})',
+ ])
+ lexer = guess_lexer(code)
+ assert lexer.__class__.__name__ == 'CMakeLexer', \
+ "header must be detected as CMake: %r" % header
+
+
+def test_guess_c_lexer():
+ code = '''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ int main(void);
+
+ int main(void) {
+ uint8_t x = 42;
+ uint8_t y = x + 1;
+
+ /* exit 1 for success! */
+ return 1;
+ }
+ '''
+ lexer = guess_lexer(code)
+ assert lexer.__class__.__name__ == 'CLexer'
+
+
+def test_cbmbasicv2_analyse_text():
+ text = "10 PRINT \"PART 1\""
+ res = CbmBasicV2Lexer.analyse_text(text)
+ assert res == 0.2
+
+
+def test_ecl_analyze_text():
+ text = r"""
+ STRING ABC -> size32_t lenAbc, const char * abc;
+ """
+ res = ECLLexer.analyse_text(text)
+ assert res == 0.01
diff --git a/tests/test_html_formatter.py b/tests/test_html_formatter.py
new file mode 100644
index 0000000..e1a02b2
--- /dev/null
+++ b/tests/test_html_formatter.py
@@ -0,0 +1,271 @@
+"""
+ Pygments HTML formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import re
+import tempfile
+from io import StringIO
+from os import path
+
+import pytest
+
+from pygments.formatters import HtmlFormatter, NullFormatter
+from pygments.formatters.html import escape_html
+from pygments.lexers import PythonLexer
+from pygments.style import Style
+
+TESTDIR = path.dirname(path.abspath(__file__))
+TESTFILE = path.join(TESTDIR, 'test_html_formatter.py')
+
+with open(TESTFILE, encoding='utf-8') as fp:
+ tokensource = list(PythonLexer().get_tokens(fp.read()))
+
+
+def test_correct_output():
+ hfmt = HtmlFormatter(nowrap=True)
+ houtfile = StringIO()
+ hfmt.format(tokensource, houtfile)
+
+ nfmt = NullFormatter()
+ noutfile = StringIO()
+ nfmt.format(tokensource, noutfile)
+
+ stripped_html = re.sub('<.*?>', '', houtfile.getvalue())
+ escaped_text = escape_html(noutfile.getvalue())
+ assert stripped_html == escaped_text
+
+
+def test_external_css():
+ # test correct behavior
+ # CSS should be in /tmp directory
+ fmt1 = HtmlFormatter(full=True, cssfile='fmt1.css', outencoding='utf-8')
+ # CSS should be in TESTDIR (TESTDIR is absolute)
+ fmt2 = HtmlFormatter(full=True, cssfile=path.join(TESTDIR, 'fmt2.css'),
+ outencoding='utf-8')
+ tfile = tempfile.NamedTemporaryFile(suffix='.html')
+ fmt1.format(tokensource, tfile)
+ try:
+ fmt2.format(tokensource, tfile)
+ assert path.isfile(path.join(TESTDIR, 'fmt2.css'))
+ except OSError:
+ # test directory not writable
+ pass
+ tfile.close()
+
+ assert path.isfile(path.join(path.dirname(tfile.name), 'fmt1.css'))
+ os.unlink(path.join(path.dirname(tfile.name), 'fmt1.css'))
+ try:
+ os.unlink(path.join(TESTDIR, 'fmt2.css'))
+ except OSError:
+ pass
+
+
+def test_all_options():
+ def check(optdict):
+ outfile = StringIO()
+ fmt = HtmlFormatter(**optdict)
+ fmt.format(tokensource, outfile)
+
+ for optdict in [
+ dict(nowrap=True),
+ dict(linenos=True, full=True),
+ dict(linenos=True, linespans='L'),
+ dict(hl_lines=[1, 5, 10, 'xxx']),
+ dict(hl_lines=[1, 5, 10], noclasses=True),
+ ]:
+ check(optdict)
+
+ for linenos in [False, 'table', 'inline']:
+ for noclasses in [False, True]:
+ for linenospecial in [0, 5]:
+ for anchorlinenos in [False, True]:
+ optdict = dict(
+ linenos=linenos,
+ noclasses=noclasses,
+ linenospecial=linenospecial,
+ anchorlinenos=anchorlinenos,
+ )
+ check(optdict)
+
+
+def test_linespans():
+ outfile = StringIO()
+ fmt = HtmlFormatter(linespans='L', anchorlinenos=True, linenos="inline")
+ fmt.format(tokensource, outfile)
+ html = outfile.getvalue()
+ assert re.search(r"""<span id="L-1">\s*<a href="#L-1"><span\s*class="linenos">\s*1</span></a>""", html)
+
+
+def test_lineanchors():
+ optdict = dict(lineanchors="foo")
+ outfile = StringIO()
+ fmt = HtmlFormatter(**optdict)
+ fmt.format(tokensource, outfile)
+ html = outfile.getvalue()
+ assert re.search("<pre>\\s*<span>\\s*</span>\\s*<a id=\"foo-1\" name=\"foo-1\" href=\"#foo-1\">", html)
+
+
+def test_lineanchors_with_startnum():
+ optdict = dict(lineanchors="foo", linenostart=5)
+ outfile = StringIO()
+ fmt = HtmlFormatter(**optdict)
+ fmt.format(tokensource, outfile)
+ html = outfile.getvalue()
+ assert re.search("<pre>\\s*<span>\\s*</span>\\s*<a id=\"foo-5\" name=\"foo-5\" href=\"#foo-5\">", html)
+
+
+def test_valid_output():
+ # test all available wrappers
+ fmt = HtmlFormatter(full=True, linenos=True, noclasses=True,
+ outencoding='utf-8')
+
+ handle, pathname = tempfile.mkstemp('.html')
+ with os.fdopen(handle, 'w+b') as tfile:
+ fmt.format(tokensource, tfile)
+ catname = os.path.join(TESTDIR, 'dtds', 'HTML4.soc')
+ try:
+ import subprocess
+ po = subprocess.Popen(['nsgmls', '-s', '-c', catname, pathname],
+ stdout=subprocess.PIPE)
+ ret = po.wait()
+ output = po.stdout.read()
+ po.stdout.close()
+ except OSError:
+ # nsgmls not available
+ pass
+ else:
+ if ret:
+ print(output)
+ assert not ret, 'nsgmls run reported errors'
+
+ os.unlink(pathname)
+
+
+def test_get_style_defs_contains_pre_style():
+ style_defs = HtmlFormatter().get_style_defs().splitlines()
+ assert style_defs[0] == 'pre { line-height: 125%; }'
+
+
+def test_get_style_defs_contains_default_line_numbers_styles():
+ style_defs = HtmlFormatter().get_style_defs().splitlines()
+
+ assert style_defs[1] == (
+ 'td.linenos .normal '
+ '{ color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }'
+ )
+ assert style_defs[2] == (
+ 'span.linenos '
+ '{ color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }'
+ )
+
+
+def test_get_style_defs_contains_style_specific_line_numbers_styles():
+ class TestStyle(Style):
+ line_number_color = '#ff0000'
+ line_number_background_color = '#0000ff'
+ line_number_special_color = '#00ff00'
+ line_number_special_background_color = '#ffffff'
+
+ style_defs = HtmlFormatter(style=TestStyle).get_style_defs().splitlines()
+
+ assert style_defs[1] == (
+ 'td.linenos .normal '
+ '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
+ )
+ assert style_defs[2] == (
+ 'span.linenos '
+ '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
+ )
+ assert style_defs[3] == (
+ 'td.linenos .special '
+ '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
+ )
+ assert style_defs[4] == (
+ 'span.linenos.special '
+ '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
+ )
+
+
+@pytest.mark.parametrize(
+ "formatter_kwargs, style_defs_args, assert_starts_with, assert_contains",
+ [
+ [{}, [], ".", []],
+ [{"cssclass": "foo"}, [], ".foo .", []],
+ [{"cssclass": "foo"}, [".bar"], ".bar .", []],
+ [{"cssclass": "foo"}, [[".bar", ".baz"]], ".ba", [".bar .", ".baz ."]],
+ ]
+)
+def test_get_token_style_defs_uses_css_prefix(
+ formatter_kwargs, style_defs_args, assert_starts_with, assert_contains
+):
+ formatter = HtmlFormatter(**formatter_kwargs)
+
+ for line in formatter.get_token_style_defs(*style_defs_args):
+ assert line.startswith(assert_starts_with)
+ for s in assert_contains:
+ assert s in line
+
+
+def test_get_background_style_defs_uses_multiple_css_prefixes():
+ formatter = HtmlFormatter()
+
+ lines = formatter.get_background_style_defs([".foo", ".bar"])
+ assert lines[0].startswith(".foo .hll, .bar .hll {")
+ assert lines[1].startswith(".foo , .bar {")
+
+
+def test_unicode_options():
+ fmt = HtmlFormatter(title='Föö',
+ cssclass='bär',
+ cssstyles='div:before { content: \'bäz\' }',
+ encoding='utf-8')
+ handle, pathname = tempfile.mkstemp('.html')
+ with os.fdopen(handle, 'w+b') as tfile:
+ fmt.format(tokensource, tfile)
+
+
+def test_ctags():
+ try:
+ import ctags
+ except ImportError:
+ # we can't check without the ctags module, but at least check the exception
+ assert pytest.raises(
+ RuntimeError, HtmlFormatter, tagsfile='support/tags'
+ )
+ else:
+ # this tagfile says that test_ctags() is on line 165, even if it isn't
+ # anymore in the actual source
+ fmt = HtmlFormatter(tagsfile='support/tags', lineanchors='L',
+ tagurlformat='%(fname)s%(fext)s')
+ outfile = StringIO()
+ fmt.format(tokensource, outfile)
+ assert '<a href="test_html_formatter.py#L-165">test_ctags</a>' \
+ in outfile.getvalue()
+
+
+def test_filename():
+ optdict = dict(filename="test.py")
+ outfile = StringIO()
+ fmt = HtmlFormatter(**optdict)
+ fmt.format(tokensource, outfile)
+ html = outfile.getvalue()
+ assert re.search("<span class=\"filename\">test.py</span><pre>", html)
+
+
+def test_debug_token_types():
+ fmt_nod_token_types = HtmlFormatter(debug_token_types=False)
+ outfile_nod_token_types = StringIO()
+ fmt_nod_token_types.format(tokensource, outfile_nod_token_types)
+ html_nod_token_types = outfile_nod_token_types.getvalue()
+ assert '<span class="n" title="Name">TESTDIR</span>' not in html_nod_token_types
+
+ fmt_debug_token_types = HtmlFormatter(debug_token_types=True)
+ outfile_debug_token_types = StringIO()
+ fmt_debug_token_types.format(tokensource, outfile_debug_token_types)
+ html_debug_token_types = outfile_debug_token_types.getvalue()
+ assert '<span class="n" title="Name">TESTDIR</span>' in html_debug_token_types
diff --git a/tests/test_html_formatter_linenos_elements.py b/tests/test_html_formatter_linenos_elements.py
new file mode 100644
index 0000000..286b60c
--- /dev/null
+++ b/tests/test_html_formatter_linenos_elements.py
@@ -0,0 +1,63 @@
+import os
+from io import StringIO
+
+import pytest
+
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import PythonLexer
+
+from .support import structural_diff
+
+TESTDIR = os.path.dirname(os.path.abspath(__file__))
+EXPECTED_OUTPUT_DIR = os.path.join(TESTDIR, "html_linenos_expected_output")
+CODE = list(PythonLexer().get_tokens("# a\n# b\n# c"))
+
+
+def single_line(text):
+ return "".join(l.strip() for l in text.splitlines())
+
+
+# Note: option `anchorlinenos` is currently ignored for `linenos=inline`
+@pytest.mark.parametrize("linenos", ["inline", "table"])
+@pytest.mark.parametrize("noclasses", ["False", "True"])
+@pytest.mark.parametrize("linenostep", ["1", "2"])
+@pytest.mark.parametrize("linenostart", ["1", "8"])
+@pytest.mark.parametrize("linenospecial", ["0", "3"])
+@pytest.mark.parametrize("anchorlinenos", ["False", "True"])
+@pytest.mark.parametrize("filename", ["", "testfilename"])
+def test_linenos_elements(
+ linenos, noclasses, linenostep, linenostart, linenospecial, anchorlinenos, filename
+):
+ options = dict(
+ linenos=linenos,
+ noclasses=noclasses,
+ linenostep=linenostep,
+ linenostart=linenostart,
+ linenospecial=linenospecial,
+ anchorlinenos=anchorlinenos,
+ filename=filename,
+ )
+
+ output = StringIO()
+ fmt = HtmlFormatter(**options)
+ fmt.format(CODE, output)
+ html = output.getvalue()
+
+ filename_parts = []
+ filename_parts.append(linenos)
+ filename_parts.append("nocls" if noclasses == "True" else "cls")
+ filename_parts.append("step_" + linenostep)
+ filename_parts.append("start_" + linenostart)
+ filename_parts.append("special_" + linenospecial)
+ filename_parts.append("anchor" if anchorlinenos == "True" else "noanchor")
+ filename_parts.append("filename" if filename else "nofilename")
+ expected_html_filename = "_".join(filename_parts) + ".html"
+
+ # with open(os.path.join(EXPECTED_OUTPUT_DIR, expected_html_filename), 'w') as f:
+ # import bs4 as BeautifulSoup
+ # f.write(str(BeautifulSoup.BeautifulSoup(html, 'html.parser')))
+
+ with open(os.path.join(EXPECTED_OUTPUT_DIR, expected_html_filename)) as f:
+ expected_html = f.read()
+
+ structural_diff.structural_diff(html, expected_html)
diff --git a/tests/test_html_lexer.py b/tests/test_html_lexer.py
new file mode 100644
index 0000000..fe99149
--- /dev/null
+++ b/tests/test_html_lexer.py
@@ -0,0 +1,131 @@
+"""
+ HTML Lexer Tests
+ ~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import time
+
+import pytest
+
+from pygments.lexers.html import HtmlLexer
+from pygments.token import Token
+
+MAX_HL_TIME = 10
+
+
+@pytest.fixture(scope='module')
+def lexer_html():
+ yield HtmlLexer()
+
+
+def test_happy_javascript_fragment(lexer_html):
+ """valid, even long Javascript fragments should still get parsed ok"""
+
+ fragment = "<script type=\"text/javascript\">"+"alert(\"hi\");"*2000+"</script>"
+ start_time = time.time()
+ tokens = list(lexer_html.get_tokens(fragment))
+ assert all(x[1] != Token.Error for x in tokens)
+ assert time.time() - start_time < MAX_HL_TIME, \
+ 'The HTML lexer might have an expensive happy-path script case'
+
+
+def test_happy_css_fragment(lexer_html):
+ """valid, even long CSS fragments should still get parsed ok"""
+
+ fragment = "<style>"+".ui-helper-hidden{display:none}"*2000+"</style>"
+ start_time = time.time()
+ tokens = list(lexer_html.get_tokens(fragment))
+ assert all(x[1] != Token.Error for x in tokens)
+ assert time.time() - start_time < MAX_HL_TIME, \
+ 'The HTML lexer might have an expensive happy-path style case'
+
+
+def test_long_unclosed_javascript_fragment(lexer_html):
+ """unclosed, long Javascript fragments should parse quickly"""
+
+ reps = 2000
+ fragment = "<script type=\"text/javascript\">"+"alert(\"hi\");"*reps
+ start_time = time.time()
+ tokens = list(lexer_html.get_tokens(fragment))
+ assert time.time() - start_time < MAX_HL_TIME, \
+ 'The HTML lexer might have an expensive error script case'
+ tokens_intro = [
+ (Token.Punctuation, '<'),
+ (Token.Name.Tag, 'script'),
+ (Token.Text, ' '),
+ (Token.Name.Attribute, 'type'),
+ (Token.Operator, '='),
+ (Token.Literal.String, '"text/javascript"'),
+ (Token.Punctuation, '>'),
+ ]
+ tokens_body = [
+ (Token.Name.Other, 'alert'),
+ (Token.Punctuation, '('),
+ (Token.Literal.String.Double, '"hi"'),
+ (Token.Punctuation, ')'),
+ (Token.Punctuation, ';'),
+ ]
+
+ # make sure we get the right opening tokens
+ assert tokens[:len(tokens_intro)] == tokens_intro
+ # and make sure we get the right body tokens even though the script is
+ # unclosed
+ assert tokens[len(tokens_intro):-1] == tokens_body * reps
+ # and of course, the newline we get for free from get_tokens
+ assert tokens[-1] == (Token.Text.Whitespace, "\n")
+
+
+def test_long_unclosed_css_fragment(lexer_html):
+ """unclosed, long CSS fragments should parse quickly"""
+
+ reps = 2000
+ fragment = "<style>"+".ui-helper-hidden{display:none}"*reps
+ start_time = time.time()
+ tokens = list(lexer_html.get_tokens(fragment))
+ assert time.time() - start_time < MAX_HL_TIME, \
+ 'The HTML lexer might have an expensive error style case'
+
+ tokens_intro = [
+ (Token.Punctuation, '<'),
+ (Token.Name.Tag, 'style'),
+ (Token.Punctuation, '>'),
+ ]
+ tokens_body = [
+ (Token.Punctuation, '.'),
+ (Token.Name.Class, 'ui-helper-hidden'),
+ (Token.Punctuation, '{'),
+ (Token.Keyword, 'display'),
+ (Token.Punctuation, ':'),
+ (Token.Keyword.Constant, 'none'),
+ (Token.Punctuation, '}'),
+ ]
+
+ # make sure we get the right opening tokens
+ assert tokens[:len(tokens_intro)] == tokens_intro
+ # and make sure we get the right body tokens even though the style block is
+ # unclosed
+ assert tokens[len(tokens_intro):-1] == tokens_body * reps
+ # and of course, the newline we get for free from get_tokens
+ assert tokens[-1] == (Token.Text.Whitespace, "\n")
+
+
+def test_unclosed_fragment_with_newline_recovery(lexer_html):
+ """unclosed Javascript fragments should recover on the next line"""
+
+ fragment = "<script type=\"text/javascript\">"+"alert(\"hi\");"*20+"\n<div>hi</div>"
+ tokens = list(lexer_html.get_tokens(fragment))
+ recovery_tokens = [
+ (Token.Punctuation, '<'),
+ (Token.Name.Tag, 'div'),
+ (Token.Punctuation, '>'),
+ (Token.Text, 'hi'),
+ (Token.Punctuation, '<'),
+ (Token.Punctuation, '/'),
+ (Token.Name.Tag, 'div'),
+ (Token.Punctuation, '>'),
+ (Token.Text, '\n'),
+ ]
+ assert tokens[-1*len(recovery_tokens):] == recovery_tokens
diff --git a/tests/test_inherit.py b/tests/test_inherit.py
new file mode 100644
index 0000000..a276378
--- /dev/null
+++ b/tests/test_inherit.py
@@ -0,0 +1,101 @@
+"""
+ Tests for inheritance in RegexLexer
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, inherit
+from pygments.token import Text
+
+
+class One(RegexLexer):
+ tokens = {
+ 'root': [
+ ('a', Text),
+ ('b', Text),
+ ],
+ }
+
+
+class Two(One):
+ tokens = {
+ 'root': [
+ ('x', Text),
+ inherit,
+ ('y', Text),
+ ],
+ }
+
+
+class Three(Two):
+ tokens = {
+ 'root': [
+ ('i', Text),
+ inherit,
+ ('j', Text),
+ ],
+ }
+
+
+class Beginning(Two):
+ tokens = {
+ 'root': [
+ inherit,
+ ('m', Text),
+ ],
+ }
+
+
+class End(Two):
+ tokens = {
+ 'root': [
+ ('m', Text),
+ inherit,
+ ],
+ }
+
+
+class Empty(One):
+ tokens = {}
+
+
+class Skipped(Empty):
+ tokens = {
+ 'root': [
+ ('x', Text),
+ inherit,
+ ('y', Text),
+ ],
+ }
+
+
+def test_single_inheritance_position():
+ t = Two()
+ pats = [x[0].__self__.pattern for x in t._tokens['root']]
+ assert ['x', 'a', 'b', 'y'] == pats
+
+
+def test_multi_inheritance_beginning():
+ t = Beginning()
+ pats = [x[0].__self__.pattern for x in t._tokens['root']]
+ assert ['x', 'a', 'b', 'y', 'm'] == pats
+
+
+def test_multi_inheritance_end():
+ t = End()
+ pats = [x[0].__self__.pattern for x in t._tokens['root']]
+ assert ['m', 'x', 'a', 'b', 'y'] == pats
+
+
+def test_multi_inheritance_position():
+ t = Three()
+ pats = [x[0].__self__.pattern for x in t._tokens['root']]
+ assert ['i', 'x', 'a', 'b', 'y', 'j'] == pats
+
+
+def test_single_inheritance_with_skip():
+ t = Skipped()
+ pats = [x[0].__self__.pattern for x in t._tokens['root']]
+ assert ['x', 'a', 'b', 'y'] == pats
diff --git a/tests/test_irc_formatter.py b/tests/test_irc_formatter.py
new file mode 100644
index 0000000..c930166
--- /dev/null
+++ b/tests/test_irc_formatter.py
@@ -0,0 +1,30 @@
+"""
+ Pygments IRC formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from io import StringIO
+
+from pygments.lexers import PythonLexer
+from pygments.formatters import IRCFormatter
+
+tokensource = list(PythonLexer().get_tokens("lambda x: 123"))
+newlinetokensource = list(PythonLexer().get_tokens("from \\\n\\\n os import path\n"))
+
+def test_correct_output():
+ hfmt = IRCFormatter()
+ houtfile = StringIO()
+ hfmt.format(tokensource, houtfile)
+
+ assert '\x0302lambda\x03 x: \x0302123\x03\n' == houtfile.getvalue()
+
+def test_linecount_output():
+ hfmt = IRCFormatter(linenos = True)
+ houtfile = StringIO()
+ hfmt.format(newlinetokensource, houtfile)
+
+ expected_out = '0001: \x0302from\x03 \\\n0002: \\\n0003: \x1d\x0310os\x03\x1d \x0302import\x03 path\n0004: '
+ assert expected_out == houtfile.getvalue()
diff --git a/tests/test_java.py b/tests/test_java.py
new file mode 100644
index 0000000..a50e862
--- /dev/null
+++ b/tests/test_java.py
@@ -0,0 +1,40 @@
+"""
+ Basic JavaLexer Test
+ ~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import time
+
+import pytest
+
+from pygments.token import String
+from pygments.lexers import JavaLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield JavaLexer()
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '""', '"abc"', '"ひらがな"', '"123"',
+ '"\\\\"', '"\\t"' '"\\""',
+ ),
+)
+def test_string_literals_positive_match(lexer, text):
+ """Test positive matches for string literals."""
+ tokens = list(lexer.get_tokens_unprocessed(text))
+ assert all([token is String for _, token, _ in tokens])
+ assert ''.join([value for _, _, value in tokens]) == text
+
+
+def test_string_literals_backtracking(lexer):
+ """Test catastrophic backtracking for string literals."""
+ start_time = time.time()
+ list(lexer.get_tokens_unprocessed('"' + '\\' * 100))
+ assert time.time() - start_time < 1, 'possible backtracking bug'
diff --git a/tests/test_javascript.py b/tests/test_javascript.py
new file mode 100644
index 0000000..05f74e2
--- /dev/null
+++ b/tests/test_javascript.py
@@ -0,0 +1,84 @@
+"""
+ Javascript tests
+ ~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.javascript import JavascriptLexer
+from pygments.token import Number
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield JavascriptLexer()
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '1', '1.', '.1', '1.1', '1e1', '1E1', '1e+1', '1E-1', '1.e1', '.1e1',
+ '0888', # octal prefix with non-octal numbers
+ )
+)
+def test_float_literal_positive_matches(lexer, text):
+ """Test literals that should be tokenized as float literals."""
+ assert list(lexer.get_tokens(text))[0] == (Number.Float, text)
+
+
+@pytest.mark.parametrize('text', ('.\u0b6a', '.', '1..', '1n', '1ee', '1e',
+ '1e-', '1e--1', '1e++1', '1e1.0'))
+def test_float_literals_negative_matches(lexer, text):
+ """Test text that should **not** be tokenized as float literals."""
+ assert list(lexer.get_tokens(text))[0] != (Number.Float, text)
+
+
+@pytest.mark.parametrize('text', ('0n', '123n'))
+def test_integer_literal_positive_matches(lexer, text):
+ """Test literals that should be tokenized as integer literals."""
+ assert list(lexer.get_tokens(text))[0] == (Number.Integer, text)
+
+
+@pytest.mark.parametrize('text', ('1N', '1', '1.0'))
+def test_integer_literals_negative_matches(lexer, text):
+ """Test text that should **not** be tokenized as integer literals."""
+ assert list(lexer.get_tokens(text))[0] != (Number.Integer, text)
+
+
+@pytest.mark.parametrize('text', ('0b01', '0B10n'))
+def test_binary_literal_positive_matches(lexer, text):
+ """Test literals that should be tokenized as binary literals."""
+ assert list(lexer.get_tokens(text))[0] == (Number.Bin, text)
+
+
+@pytest.mark.parametrize('text', ('0b0N', '0b', '0bb', '0b2'))
+def test_binary_literals_negative_matches(lexer, text):
+ """Test text that should **not** be tokenized as binary literals."""
+ assert list(lexer.get_tokens(text))[0] != (Number.Bin, text)
+
+
+@pytest.mark.parametrize('text', ('017', '071n', '0o11', '0O77n'))
+def test_octal_literal_positive_matches(lexer, text):
+ """Test literals that should be tokenized as octal literals."""
+ assert list(lexer.get_tokens(text))[0] == (Number.Oct, text)
+
+
+@pytest.mark.parametrize('text', ('01N', '089', '098n', '0o', '0OO', '0o88', '0O88n'))
+def test_octal_literals_negative_matches(lexer, text):
+ """Test text that should **not** be tokenized as octal literals."""
+ assert list(lexer.get_tokens(text))[0] != (Number.Oct, text)
+
+
+@pytest.mark.parametrize('text', ('0x01', '0Xefn', '0x0EF'))
+def test_hexadecimal_literal_positive_matches(lexer, text):
+ """Test literals that should be tokenized as hexadecimal literals."""
+ assert list(lexer.get_tokens(text))[0] == (Number.Hex, text)
+
+
+@pytest.mark.parametrize('text', ('0x0N', '0x', '0Xx', '0xg', '0xhn'))
+def test_hexadecimal_literals_negative_matches(lexer, text):
+ """Test text that should **not** be tokenized as hexadecimal literals."""
+ assert list(lexer.get_tokens(text))[0] != (Number.Hex, text)
diff --git a/tests/test_latex_formatter.py b/tests/test_latex_formatter.py
new file mode 100644
index 0000000..42c5eda
--- /dev/null
+++ b/tests/test_latex_formatter.py
@@ -0,0 +1,107 @@
+"""
+ Pygments LaTeX formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import tempfile
+from os import path
+from io import StringIO
+from textwrap import dedent
+
+import pytest
+
+from pygments.formatters import LatexFormatter
+from pygments.formatters.latex import LatexEmbeddedLexer
+from pygments.lexers import PythonLexer, PythonConsoleLexer
+from pygments.token import Token
+
+TESTDIR = path.dirname(path.abspath(__file__))
+TESTFILE = path.join(TESTDIR, 'test_latex_formatter.py')
+
+
+def test_correct_output():
+ with open(TESTFILE) as fp:
+ tokensource = list(PythonLexer().get_tokens(fp.read()))
+ hfmt = LatexFormatter(nowrap=True)
+ houtfile = StringIO()
+ hfmt.format(tokensource, houtfile)
+
+ assert r'\begin{Verbatim}' not in houtfile.getvalue()
+ assert r'\end{Verbatim}' not in houtfile.getvalue()
+
+
+def test_valid_output():
+ with open(TESTFILE) as fp:
+ tokensource = list(PythonLexer().get_tokens(fp.read()))
+ fmt = LatexFormatter(full=True, encoding='latin1')
+
+ handle, pathname = tempfile.mkstemp('.tex')
+ # place all output files in /tmp too
+ old_wd = os.getcwd()
+ os.chdir(os.path.dirname(pathname))
+ tfile = os.fdopen(handle, 'wb')
+ fmt.format(tokensource, tfile)
+ tfile.close()
+ try:
+ import subprocess
+ po = subprocess.Popen(['latex', '-interaction=nonstopmode',
+ pathname], stdout=subprocess.PIPE)
+ ret = po.wait()
+ output = po.stdout.read()
+ po.stdout.close()
+ except OSError as e:
+ # latex not available
+ pytest.skip(str(e))
+ else:
+ if ret:
+ print(output)
+ assert not ret, 'latex run reported errors'
+
+ os.unlink(pathname)
+ os.chdir(old_wd)
+
+
+def test_embedded_lexer():
+ # Latex surrounded by '|' should be Escaped
+ lexer = LatexEmbeddedLexer('|', '|', PythonConsoleLexer())
+
+ # similar to gh-1516
+ src = dedent("""\
+ >>> x = 1
+ >>> y = mul(x, |$z^2$|) # these |pipes| are untouched
+ >>> y
+ |$1 + z^2$|""")
+
+ assert list(lexer.get_tokens(src)) == [
+ (Token.Generic.Prompt, '>>> '),
+ (Token.Name, 'x'),
+ (Token.Text, ' '),
+ (Token.Operator, '='),
+ (Token.Text, ' '),
+ (Token.Literal.Number.Integer, '1'),
+ (Token.Text.Whitespace, '\n'),
+ (Token.Generic.Prompt, '>>> '),
+ (Token.Name, 'y'),
+ (Token.Text, ' '),
+ (Token.Operator, '='),
+ (Token.Text, ' '),
+ (Token.Name, 'mul'),
+ (Token.Punctuation, '('),
+ (Token.Name, 'x'),
+ (Token.Punctuation, ','),
+ (Token.Text, ' '),
+ (Token.Escape, '$z^2$'),
+ (Token.Punctuation, ')'),
+ (Token.Text, ' '),
+ (Token.Comment.Single, '# these |pipes| are untouched'), # note: not Token.Escape
+ (Token.Text.Whitespace, '\n'),
+ (Token.Generic.Prompt, '>>> '),
+ (Token.Name, 'y'),
+ (Token.Text.Whitespace, '\n'),
+ (Token.Escape, '$1 + z^2$'),
+ (Token.Generic.Output, '\n'),
+ ]
diff --git a/tests/test_markdown_lexer.py b/tests/test_markdown_lexer.py
new file mode 100644
index 0000000..46b2911
--- /dev/null
+++ b/tests/test_markdown_lexer.py
@@ -0,0 +1,178 @@
+"""
+ Pygments Markdown lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+from pygments.token import Generic, Token, String
+
+from pygments.lexers.markup import MarkdownLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield MarkdownLexer()
+
+
+def assert_same_text(lexer, text):
+ """Show that lexed markdown does not remove any content. """
+ tokens = list(lexer.get_tokens_unprocessed(text))
+ output = ''.join(t[2] for t in tokens)
+ assert text == output
+
+
+def test_code_fence(lexer):
+ assert_same_text(lexer, r'```\nfoo\n```\n')
+
+
+def test_code_fence_gsm(lexer):
+ assert_same_text(lexer, r'```markdown\nfoo\n```\n')
+
+
+def test_code_fence_gsm_with_no_lexer(lexer):
+ assert_same_text(lexer, r'```invalid-lexer\nfoo\n```\n')
+
+
+def test_invalid_atx_heading(lexer):
+ fragments = (
+ '#',
+ 'a #',
+ '*#',
+ )
+
+ for fragment in fragments:
+ for token, _ in lexer.get_tokens(fragment):
+ assert token != Generic.Heading
+
+
+def test_atx_heading(lexer):
+ fragments = (
+ '#Heading',
+ '# Heading',
+ '# Another heading',
+ '# Another # heading',
+ '# Heading #',
+ )
+
+ for fragment in fragments:
+ tokens = [
+ (Generic.Heading, fragment),
+ (Token.Text, '\n'),
+ ]
+ assert list(lexer.get_tokens(fragment)) == tokens
+
+
+def test_invalid_atx_subheading(lexer):
+ fragments = (
+ '##',
+ 'a ##',
+ '*##',
+ '####### too many hashes'
+ )
+
+ for fragment in fragments:
+ for token, _ in lexer.get_tokens(fragment):
+ assert token != Generic.Subheading
+
+
+def test_atx_subheading(lexer):
+ fragments = (
+ '##Subheading',
+ '## Subheading',
+ '### Subheading',
+ '#### Subheading',
+ '##### Subheading',
+ '###### Subheading',
+ '## Another subheading',
+ '## Another ## subheading',
+ '###### Subheading #',
+ '###### Subheading ######',
+ )
+
+ for fragment in fragments:
+ tokens = [
+ (Generic.Subheading, fragment),
+ (Token.Text, '\n'),
+ ]
+ assert list(lexer.get_tokens(fragment)) == tokens
+
+
+def test_invalid_setext_heading(lexer):
+ fragments = (
+ 'Heading\n',
+ 'Heading\n_',
+ 'Heading\n =====',
+ 'Heading\na=====',
+ '=====',
+ '\n=\n',
+ 'Heading\n=====Text'
+ )
+
+ for fragment in fragments:
+ for token, _ in lexer.get_tokens(fragment):
+ assert token != Generic.Heading
+
+
+def test_setext_heading(lexer):
+ fragments = (
+ 'Heading\n=',
+ 'Heading\n=======',
+ 'Heading\n==========',
+ )
+
+ for fragment in fragments:
+ tokens = [
+ (Generic.Heading, fragment.split('\n')[0]),
+ (Token.Text, '\n'),
+ (Generic.Heading, fragment.split('\n')[1]),
+ (Token.Text, '\n'),
+ ]
+ assert list(lexer.get_tokens(fragment)) == tokens
+
+
+def test_invalid_setext_subheading(lexer):
+ fragments = (
+ 'Subheading\n',
+ 'Subheading\n_',
+ 'Subheading\n -----',
+ 'Subheading\na-----',
+ '-----',
+ '\n-\n',
+ 'Subheading\n-----Text'
+ )
+
+ for fragment in fragments:
+ for token, _ in lexer.get_tokens(fragment):
+ assert token != Generic.Subheading
+
+
+def test_setext_subheading(lexer):
+ fragments = (
+ 'Subheading\n-',
+ 'Subheading\n----------',
+ 'Subheading\n-----------',
+ )
+
+ for fragment in fragments:
+ tokens = [
+ (Generic.Subheading, fragment.split('\n')[0]),
+ (Token.Text, '\n'),
+ (Generic.Subheading, fragment.split('\n')[1]),
+ (Token.Text, '\n'),
+ ]
+ assert list(lexer.get_tokens(fragment)) == tokens
+
+
+def test_invalid_code_block(lexer):
+ fragments = (
+ '```code```',
+ 'prefix not allowed before ```\ncode block\n```'
+ ' code',
+ )
+
+ for fragment in fragments:
+ for token, _ in lexer.get_tokens(fragment):
+ assert token != String.Backtick
diff --git a/tests/test_modeline.py b/tests/test_modeline.py
new file mode 100644
index 0000000..86acbd7
--- /dev/null
+++ b/tests/test_modeline.py
@@ -0,0 +1,20 @@
+"""
+ Tests for the vim modeline feature
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pygments import modeline
+
+
+def test_modelines():
+ for buf in [
+ 'vi: ft=python' + '\n' * 8,
+ 'vi: ft=python' + '\n' * 8,
+ '\n\n\n\nvi=8: syntax=python' + '\n' * 8,
+ '\n' * 8 + 'ex: filetype=python',
+ '\n' * 8 + 'vim: some,other,syn=python\n\n\n\n'
+ ]:
+ assert modeline.get_filetype_from_buffer(buf) == 'python'
diff --git a/tests/test_mysql.py b/tests/test_mysql.py
new file mode 100644
index 0000000..39da8f5
--- /dev/null
+++ b/tests/test_mysql.py
@@ -0,0 +1,273 @@
+"""
+ Pygments MySQL lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.sql import MySqlLexer
+
+from pygments.token import Comment, Keyword, Literal, Name, Number, Operator, \
+ Punctuation, String, Whitespace
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield MySqlLexer()
+
+
+@pytest.mark.parametrize('text', ('1', '22', '22 333', '22 a', '22+', '22)',
+ '22\n333', '22\r\n333'))
+def test_integer_literals_positive_match(lexer, text):
+ """Validate that integer literals are tokenized as integers."""
+ token = list(lexer.get_tokens(text))[0]
+ assert token[0] == Number.Integer
+ assert token[1] in {'1', '22'}
+
+
+@pytest.mark.parametrize('text', ('1a', '1A', '1.', '1ひ', '1$', '1_',
+ '1\u0080', '1\uffff'))
+def test_integer_literals_negative_match(lexer, text):
+ """Validate that non-integer texts are not matched as integers."""
+ assert list(lexer.get_tokens(text))[0][0] != Number.Integer
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '.123', '1.23', '123.',
+ '1e10', '1.0e10', '1.e-10', '.1e+10',
+ ),
+)
+def test_float_literals(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Number.Float, text)
+
+
+@pytest.mark.parametrize('text', ("X'0af019'", "x'0AF019'", "0xaf019"))
+def test_hexadecimal_literals(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Number.Hex, text)
+
+
+@pytest.mark.parametrize('text', ("B'010'", "b'010'", "0b010"))
+def test_binary_literals(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Number.Bin, text)
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ "{d'2020-01-01'}", "{ d ' 2020^01@01 ' }",
+ "{t'8 9:10:11'}", "{ t ' 09:10:11.12 ' }", "{ t ' 091011 ' }",
+ '{ts"2020-01-01 09:10:11"}', "{ ts ' 2020@01/01 09:10:11 ' }",
+ ),
+)
+def test_temporal_literals(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Literal.Date, text)
+
+
+@pytest.mark.parametrize(
+ 'text, expected_types',
+ (
+ (r"'a'", (String.Single,) * 3),
+ (r"""'""'""", (String.Single,) * 3),
+ (r"''''", (String.Single, String.Escape, String.Single)),
+ (r"'\''", (String.Single, String.Escape, String.Single)),
+ (r'"a"', (String.Double,) * 3),
+ (r'''"''"''', (String.Double,) * 3),
+ (r'""""', (String.Double, String.Escape, String.Double)),
+ (r'"\""', (String.Double, String.Escape, String.Double)),
+ ),
+)
+def test_string_literals(lexer, text, expected_types):
+ tokens = list(lexer.get_tokens(text))[:len(expected_types)]
+ assert all(t[0] == e for t, e in zip(tokens, expected_types))
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ "@a", "@1", "@._.$",
+ "@'?'", """@'abc''def"`ghi'""",
+ '@"#"', '''@"abc""def'`ghi"''',
+ '@`^`', """@`abc``def'"ghi`""",
+ "@@timestamp",
+ "@@session.auto_increment_offset",
+ "@@global.auto_increment_offset",
+ "@@persist.auto_increment_offset",
+ "@@persist_only.auto_increment_offset",
+ '?',
+ ),
+)
+def test_variables(lexer, text):
+ tokens = list(lexer.get_tokens(text))
+ assert all(t[0] == Name.Variable for t in tokens[:-1])
+ assert ''.join([t[1] for t in tokens]).strip() == text.strip()
+
+
+@pytest.mark.parametrize('text', ('true', 'false', 'null', 'unknown'))
+def test_constants(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Name.Constant, text)
+
+
+@pytest.mark.parametrize('text', ('-- abc', '--\tabc', '#abc'))
+def test_comments_single_line(lexer, text):
+ # Test the standalone comment.
+ tokens = list(lexer.get_tokens(text))
+ assert tokens[0] == (Comment.Single, text)
+
+ # Test the comment with mixed tokens.
+ tokens = list(lexer.get_tokens('select' + text + '\nselect'))
+ assert tokens[0] == (Keyword, 'select')
+ assert tokens[1] == (Comment.Single, text)
+ assert tokens[-2] == (Keyword, 'select')
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '/**/a', '/*a*b/c*/a', '/*\nabc\n*/a',
+ '/* /* */a'
+ )
+)
+def test_comments_multi_line(lexer, text):
+ tokens = list(lexer.get_tokens(text))
+ assert all(token[0] == Comment.Multiline for token in tokens[:-2])
+ assert ''.join(token[1] for token in tokens).strip() == text.strip()
+
+ # Validate nested comments are not supported.
+ assert tokens[-2][0] != Comment.Multiline
+
+
+@pytest.mark.parametrize(
+ 'text', ('BKA', 'SEMIJOIN'))
+def test_optimizer_hints(lexer, text):
+ good = '/*+ ' + text + '(), */'
+ ignore = '/* ' + text + ' */'
+ bad1 = '/*+ a' + text + '() */'
+ bad2 = '/*+ ' + text + 'a */'
+ assert (Comment.Preproc, text) in lexer.get_tokens(good)
+ assert (Comment.Preproc, text) not in lexer.get_tokens(ignore)
+ assert (Comment.Preproc, text) not in lexer.get_tokens(bad1)
+ assert (Comment.Preproc, text) not in lexer.get_tokens(bad2)
+
+
+@pytest.mark.parametrize(
+ 'text, expected_types',
+ (
+ # SET exceptions
+ ('SET', (Keyword,)),
+ ('SET abc = 1;', (Keyword,)),
+ ('SET @abc = 1;', (Keyword,)),
+ ('CHARACTER SET latin1', (Keyword, Whitespace, Keyword,)),
+ ('SET("r", "g", "b")', (Keyword.Type, Punctuation)),
+ ('SET ("r", "g", "b")', (Keyword.Type, Whitespace, Punctuation)),
+ ),
+)
+def test_exceptions(lexer, text, expected_types):
+ tokens = list(lexer.get_tokens(text))[:len(expected_types)]
+ assert all(t[0] == e for t, e in zip(tokens, expected_types))
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ 'SHOW', 'CREATE', 'ALTER', 'DROP',
+ 'SELECT', 'INSERT', 'UPDATE', 'DELETE',
+ 'WHERE', 'GROUP', 'ORDER', 'BY', 'AS',
+ 'DISTINCT', 'JOIN', 'WITH', 'RECURSIVE',
+ 'PARTITION', 'NTILE', 'MASTER_PASSWORD', 'XA',
+ 'REQUIRE_TABLE_PRIMARY_KEY_CHECK', 'STREAM',
+ ),
+)
+def test_keywords(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Keyword, text)
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ # Standard
+ 'INT(', 'VARCHAR(', 'ENUM(', 'DATETIME', 'GEOMETRY', 'POINT', 'JSON',
+ # Aliases and compatibility
+ 'FIXED', 'MEDIUMINT', 'INT3', 'REAL', 'SERIAL',
+ 'LONG', 'NATIONAL', 'PRECISION', 'VARYING',
+ ),
+)
+def test_data_types(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Keyword.Type, text.strip('('))
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ # Common
+ 'CAST', 'CONCAT_WS', 'DAYNAME', 'IFNULL', 'NOW', 'SUBSTR',
+ # Less common
+ 'CAN_ACCESS_COLUMN', 'JSON_CONTAINS_PATH', 'ST_GEOMFROMGEOJSON',
+ ),
+)
+def test_functions(lexer, text):
+ assert list(lexer.get_tokens(text + '('))[0] == (Name.Function, text)
+ assert list(lexer.get_tokens(text + ' ('))[0] == (Name.Function, text)
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ 'abc_$123', '上市年限', 'ひらがな', '123_$abc', '123ひらがな',
+ ),
+)
+def test_schema_object_names_unquoted(lexer, text):
+ tokens = list(lexer.get_tokens(text))[:-1]
+ assert all(token[0] == Name for token in tokens)
+ assert ''.join(token[1] for token in tokens) == text
+
+
+@pytest.mark.parametrize(
+ 'text',
+ (
+ '`a`', '`1`', '`上市年限`', '`ひらがな`', '`select`', '`concat(`',
+ '`-- `', '`/*`', '`#`',
+ ),
+)
+def test_schema_object_names_quoted(lexer, text):
+ tokens = list(lexer.get_tokens(text))[:-1]
+ assert tokens[0] == (Name.Quoted, '`')
+ assert tokens[1] == (Name.Quoted, text[1:-1])
+ assert tokens[2] == (Name.Quoted, '`')
+ assert ''.join(token[1] for token in tokens) == text
+
+
+@pytest.mark.parametrize('text', ('````', ))
+def test_schema_object_names_quoted_escaped(lexer, text):
+ """Test quoted schema object names with escape sequences."""
+ tokens = list(lexer.get_tokens(text))[:-1]
+ assert tokens[0] == (Name.Quoted, '`')
+ assert tokens[1] == (Name.Quoted.Escape, text[1:-1])
+ assert tokens[2] == (Name.Quoted, '`')
+ assert ''.join(token[1] for token in tokens) == text
+
+
+@pytest.mark.parametrize(
+ 'text',
+ ('+', '*', '/', '%', '&&', ':=', '!', '<', '->>', '^', '|', '~'),
+)
+def test_operators(lexer, text):
+ assert list(lexer.get_tokens(text))[0] == (Operator, text)
+
+
+@pytest.mark.parametrize(
+ 'text, expected_types',
+ (
+ ('abc.efg', (Name, Punctuation, Name)),
+ ('abc,efg', (Name, Punctuation, Name)),
+ ('MAX(abc)', (Name.Function, Punctuation, Name, Punctuation)),
+ ('efg;', (Name, Punctuation)),
+ ),
+)
+def test_punctuation(lexer, text, expected_types):
+ tokens = list(lexer.get_tokens(text))[:len(expected_types)]
+ assert all(t[0] == e for t, e in zip(tokens, expected_types))
diff --git a/tests/test_pangomarkup_formatter.py b/tests/test_pangomarkup_formatter.py
new file mode 100644
index 0000000..e468546
--- /dev/null
+++ b/tests/test_pangomarkup_formatter.py
@@ -0,0 +1,44 @@
+"""
+ Pygments Pango Markup formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments import highlight
+from pygments.formatters import PangoMarkupFormatter
+from pygments.lexers import JavascriptLexer
+
+INPUT = r"""
+function foobar(a, b) {
+ if (a > b) {
+ return a & b;
+ }
+ if (a < b) {
+ return true;
+ }
+ console.log("single quote ' and double quote \"")
+ console.log('single quote \' and double quote "')
+ // comment with äöü ç
+}
+"""
+
+OUTPUT = r"""<tt><span fgcolor="#"><b>function</b></span><span fgcolor="#"> </span>foobar(a,<span fgcolor="#"> </span>b)<span fgcolor="#"> </span>{<span fgcolor="#">
+ </span><span fgcolor="#"><b>if</b></span><span fgcolor="#"> </span>(a<span fgcolor="#"> </span><span fgcolor="#">></span><span fgcolor="#"> </span>b)<span fgcolor="#"> </span>{<span fgcolor="#">
+ </span><span fgcolor="#"><b>return</b></span><span fgcolor="#"> </span>a<span fgcolor="#"> </span><span fgcolor="#">&amp;</span><span fgcolor="#"> </span>b;<span fgcolor="#">
+ </span>}<span fgcolor="#">
+ </span><span fgcolor="#"><b>if</b></span><span fgcolor="#"> </span>(a<span fgcolor="#"> </span><span fgcolor="#">&lt;</span><span fgcolor="#"> </span>b)<span fgcolor="#"> </span>{<span fgcolor="#">
+ </span><span fgcolor="#"><b>return</b></span><span fgcolor="#"> </span><span fgcolor="#"><b>true</b></span>;<span fgcolor="#">
+ </span>}<span fgcolor="#">
+ </span>console.log(<span fgcolor="#">"single quote ' and double quote \""</span>)<span fgcolor="#">
+ </span>console.log(<span fgcolor="#">'single quote \' and double quote "'</span>)<span fgcolor="#">
+ </span><span fgcolor="#"><i>// comment with äöü ç</i></span><span fgcolor="#">
+</span>}<span fgcolor="#">
+</span></tt>"""
+
+def test_correct_output():
+ markup = highlight(INPUT, JavascriptLexer(), PangoMarkupFormatter())
+ assert OUTPUT == re.sub('<span fgcolor="#[^"]{6}">', '<span fgcolor="#">', markup)
diff --git a/tests/test_perllexer.py b/tests/test_perllexer.py
new file mode 100644
index 0000000..21bf749
--- /dev/null
+++ b/tests/test_perllexer.py
@@ -0,0 +1,190 @@
+"""
+ Pygments regex lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import time
+
+import pytest
+
+from pygments.token import Keyword, Name, String, Text
+from pygments.lexers.perl import PerlLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield PerlLexer()
+
+
+# Test runaway regexes.
+# A previous version of the Perl lexer would spend a great deal of
+# time backtracking when given particular strings. These tests show that
+# the runaway backtracking doesn't happen any more (at least for the given
+# cases).
+
+
+# Test helpers.
+
+def assert_single_token(lexer, s, token):
+ """Show that a given string generates only one token."""
+ tokens = list(lexer.get_tokens_unprocessed(s))
+ assert len(tokens) == 1
+ assert s == tokens[0][2]
+ assert token == tokens[0][1]
+
+
+def assert_tokens(lexer, strings, expected_tokens):
+ """Show that a given string generates the expected tokens."""
+ tokens = list(lexer.get_tokens_unprocessed(''.join(strings)))
+ parsed_strings = [t[2] for t in tokens]
+ assert parsed_strings == strings
+ parsed_tokens = [t[1] for t in tokens]
+ assert parsed_tokens == expected_tokens
+
+
+def assert_fast_tokenization(lexer, s):
+ """Show that a given string is tokenized quickly."""
+ start = time.time()
+ tokens = list(lexer.get_tokens_unprocessed(s))
+ end = time.time()
+ # Isn't 10 seconds kind of a long time? Yes, but we don't want false
+ # positives when the tests are starved for CPU time.
+ if end-start > 10:
+ pytest.fail('tokenization took too long')
+ return tokens
+
+
+# Strings.
+
+def test_single_quote_strings(lexer):
+ assert_single_token(lexer, r"'foo\tbar\\\'baz'", String)
+ assert_fast_tokenization(lexer, "'" + '\\'*999)
+
+
+def test_double_quote_strings(lexer):
+ assert_single_token(lexer, r'"foo\tbar\\\"baz"', String)
+ assert_fast_tokenization(lexer, '"' + '\\'*999)
+
+
+def test_backtick_strings(lexer):
+ assert_single_token(lexer, r'`foo\tbar\\\`baz`', String.Backtick)
+ assert_fast_tokenization(lexer, '`' + '\\'*999)
+
+
+# Regex matches with various delimiters.
+
+def test_match(lexer):
+ assert_single_token(lexer, r'/aa\tbb/', String.Regex)
+ assert_fast_tokenization(lexer, '/' + '\\'*999)
+
+
+def test_match_with_slash(lexer):
+ assert_tokens(lexer, ['m', '/\n\\t\\\\/'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm/xxx\n' + '\\'*999)
+
+
+def test_match_with_bang(lexer):
+ assert_tokens(lexer, ['m', r'!aa\t\!bb!'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm!' + '\\'*999)
+
+
+def test_match_with_brace(lexer):
+ assert_tokens(lexer, ['m', r'{aa\t\}bb}'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm{' + '\\'*999)
+
+
+def test_match_with_angle_brackets(lexer):
+ assert_tokens(lexer, ['m', r'<aa\t\>bb>'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm<' + '\\'*999)
+
+
+def test_match_with_parenthesis(lexer):
+ assert_tokens(lexer, ['m', r'(aa\t\)bb)'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm(' + '\\'*999)
+
+
+def test_match_with_at_sign(lexer):
+ assert_tokens(lexer, ['m', r'@aa\t\@bb@'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm@' + '\\'*999)
+
+
+def test_match_with_percent_sign(lexer):
+ assert_tokens(lexer, ['m', r'%aa\t\%bb%'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm%' + '\\'*999)
+
+
+def test_match_with_dollar_sign(lexer):
+ assert_tokens(lexer, ['m', r'$aa\t\$bb$'], [String.Regex, String.Regex])
+ assert_fast_tokenization(lexer, 'm$' + '\\'*999)
+
+
+# Regex substitutions with various delimeters.
+
+def test_substitution_with_slash(lexer):
+ assert_single_token(lexer, 's/aaa/bbb/g', String.Regex)
+ assert_fast_tokenization(lexer, 's/foo/' + '\\'*999)
+
+
+def test_substitution_with_at_sign(lexer):
+ assert_single_token(lexer, r's@aaa@bbb@g', String.Regex)
+ assert_fast_tokenization(lexer, 's@foo@' + '\\'*999)
+
+
+def test_substitution_with_percent_sign(lexer):
+ assert_single_token(lexer, r's%aaa%bbb%g', String.Regex)
+ assert_fast_tokenization(lexer, 's%foo%' + '\\'*999)
+
+
+def test_substitution_with_brace(lexer):
+ assert_single_token(lexer, r's{aaa}', String.Regex)
+ assert_fast_tokenization(lexer, 's{' + '\\'*999)
+
+
+def test_substitution_with_angle_bracket(lexer):
+ assert_single_token(lexer, r's<aaa>', String.Regex)
+ assert_fast_tokenization(lexer, 's<' + '\\'*999)
+
+
+def test_substitution_with_square_bracket(lexer):
+ assert_single_token(lexer, r's[aaa]', String.Regex)
+ assert_fast_tokenization(lexer, 's[' + '\\'*999)
+
+
+def test_substitution_with_parenthesis(lexer):
+ assert_single_token(lexer, r's(aaa)', String.Regex)
+ assert_fast_tokenization(lexer, 's(' + '\\'*999)
+
+
+# Namespaces/modules
+
+def test_package_statement(lexer):
+ assert_tokens(lexer, ['package', ' ', 'Foo'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+ assert_tokens(lexer, ['package', ' ', 'Foo::Bar'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+
+
+def test_use_statement(lexer):
+ assert_tokens(lexer, ['use', ' ', 'Foo'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+ assert_tokens(lexer, ['use', ' ', 'Foo::Bar'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+
+
+def test_no_statement(lexer):
+ assert_tokens(lexer, ['no', ' ', 'Foo'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+ assert_tokens(lexer, ['no', ' ', 'Foo::Bar'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+
+
+def test_require_statement(lexer):
+ assert_tokens(lexer, ['require', ' ', 'Foo'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+ assert_tokens(lexer, ['require', ' ', 'Foo::Bar'],
+ [Keyword, Text.Whitespace, Name.Namespace])
+ assert_tokens(lexer, ['require', ' ', '"Foo/Bar.pm"'],
+ [Keyword, Text.Whitespace, String])
diff --git a/tests/test_procfile.py b/tests/test_procfile.py
new file mode 100644
index 0000000..d919771
--- /dev/null
+++ b/tests/test_procfile.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+"""
+ Basic ProcfileLexer Test
+ ~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.token import Name, Punctuation, Text
+from pygments.lexers.procfile import ProcfileLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield ProcfileLexer()
+
+
+def test_basic_line(lexer):
+ text = 'task: executable --options'
+
+ tokens = lexer.get_tokens(text)
+
+ for index, token in enumerate(tokens):
+ if index == 0:
+ assert token == (Name.Label, 'task')
+ elif index == 1:
+ assert token == (Punctuation, ':')
+ else:
+ assert token[0] in (Text, Text.Whitespace)
+
+
+def test_environment_variable(lexer):
+ text = '$XDG_SESSION_PATH'
+
+ token = list(lexer.get_tokens(text))[0]
+
+ assert token == (Name.Variable, text)
diff --git a/tests/test_raw_token.py b/tests/test_raw_token.py
new file mode 100644
index 0000000..bae5a49
--- /dev/null
+++ b/tests/test_raw_token.py
@@ -0,0 +1,68 @@
+import bz2
+import gzip
+
+from pygments import highlight
+from pygments.formatters import HtmlFormatter, RawTokenFormatter
+from pygments.lexers import PythonLexer, RawTokenLexer
+
+
+def test_raw_token():
+ code = "2 + α"
+ raw = highlight(code, PythonLexer(), RawTokenFormatter())
+ html = highlight(code, PythonLexer(), HtmlFormatter())
+
+ assert highlight(raw, RawTokenLexer(), RawTokenFormatter()) == raw
+ assert highlight(raw, RawTokenLexer(), HtmlFormatter()) == html
+ assert highlight(raw.decode(), RawTokenLexer(), HtmlFormatter()) == html
+
+ raw_gz = highlight(code, PythonLexer(), RawTokenFormatter(compress="gz"))
+ assert gzip.decompress(raw_gz) == raw
+ assert highlight(raw_gz, RawTokenLexer(compress="gz"), RawTokenFormatter()) == raw
+ assert (
+ highlight(
+ raw_gz.decode("latin1"), RawTokenLexer(compress="gz"), RawTokenFormatter()
+ )
+ == raw
+ )
+
+ raw_bz2 = highlight(code, PythonLexer(), RawTokenFormatter(compress="bz2"))
+ assert bz2.decompress(raw_bz2) == raw
+ assert highlight(raw_bz2, RawTokenLexer(compress="bz2"), RawTokenFormatter()) == raw
+ assert (
+ highlight(
+ raw_bz2.decode("latin1"), RawTokenLexer(compress="bz2"), RawTokenFormatter()
+ )
+ == raw
+ )
+
+
+def test_invalid_raw_token():
+ # These should not throw exceptions.
+ assert (
+ highlight("Tolkien", RawTokenLexer(), RawTokenFormatter())
+ == b"Token.Error\t'Tolkien\\n'\n"
+ )
+ assert (
+ highlight("Tolkien\t'x'", RawTokenLexer(), RawTokenFormatter())
+ == b"Token\t'x'\n"
+ )
+ assert (
+ highlight("Token.Text\t42", RawTokenLexer(), RawTokenFormatter())
+ == b"Token.Error\t'Token.Text\\t42\\n'\n"
+ )
+ assert (
+ highlight("Token.Text\t'", RawTokenLexer(), RawTokenFormatter())
+ == b'Token.Error\t"Token.Text\\t\'\\n"\n'
+ )
+ assert (
+ highlight("Token.Text\t'α'", RawTokenLexer(), RawTokenFormatter())
+ == b"Token.Text\t'\\u03b1'\n"
+ )
+ assert (
+ highlight("Token.Text\tu'α'", RawTokenLexer(), RawTokenFormatter())
+ == b"Token.Text\t'\\u03b1'\n"
+ )
+ assert (
+ highlight(b"Token.Text\t'\xff'", RawTokenLexer(), RawTokenFormatter())
+ == b"Token.Text\t'\\xff'\n"
+ )
diff --git a/tests/test_regexlexer.py b/tests/test_regexlexer.py
new file mode 100644
index 0000000..1b9639f
--- /dev/null
+++ b/tests/test_regexlexer.py
@@ -0,0 +1,65 @@
+"""
+ Pygments regex lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.token import Text, Whitespace
+from pygments.lexer import RegexLexer, default
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield MyLexer()
+
+
+class MyLexer(RegexLexer):
+ """Test tuple state transitions including #pop."""
+ tokens = {
+ 'root': [
+ ('a', Text.Root, 'rag'),
+ ('e', Text.Root),
+ ('#', Text.Root, '#pop'),
+ ('@', Text.Root, ('#pop', '#pop')),
+ default(('beer', 'beer'))
+ ],
+ 'beer': [
+ ('d', Text.Beer, ('#pop', '#pop')),
+ ],
+ 'rag': [
+ ('b', Text.Rag, '#push'),
+ ('c', Text.Rag, ('#pop', 'beer')),
+ ],
+ }
+
+
+def test_tuple(lexer):
+ toks = list(lexer.get_tokens_unprocessed('abcde'))
+ assert toks == [
+ (0, Text.Root, 'a'), (1, Text.Rag, 'b'), (2, Text.Rag, 'c'),
+ (3, Text.Beer, 'd'), (4, Text.Root, 'e')]
+
+
+def test_multiline(lexer):
+ toks = list(lexer.get_tokens_unprocessed('a\ne'))
+ assert toks == [
+ (0, Text.Root, 'a'), (1, Whitespace, '\n'), (2, Text.Root, 'e')]
+
+
+def test_default(lexer):
+ toks = list(lexer.get_tokens_unprocessed('d'))
+ assert toks == [(0, Text.Beer, 'd')]
+
+
+def test_pop_empty_regular(lexer):
+ toks = list(lexer.get_tokens_unprocessed('#e'))
+ assert toks == [(0, Text.Root, '#'), (1, Text.Root, 'e')]
+
+
+def test_pop_empty_tuple(lexer):
+ toks = list(lexer.get_tokens_unprocessed('@e'))
+ assert toks == [(0, Text.Root, '@'), (1, Text.Root, 'e')]
diff --git a/tests/test_regexopt.py b/tests/test_regexopt.py
new file mode 100644
index 0000000..2116467
--- /dev/null
+++ b/tests/test_regexopt.py
@@ -0,0 +1,102 @@
+"""
+ Tests for pygments.regexopt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+import random
+from itertools import combinations_with_replacement
+
+from pygments.regexopt import regex_opt
+
+ALPHABET = ['a', 'b', 'c', 'd', 'e']
+
+N_TRIES = 15
+
+
+def generate_keywordlist(length):
+ return [''.join(p) for p in
+ combinations_with_replacement(ALPHABET, length)]
+
+
+def test_randomly():
+ # generate a list of all possible keywords of a certain length using
+ # a restricted alphabet, then choose some to match and make sure only
+ # those do
+ for n in range(3, N_TRIES):
+ kwlist = generate_keywordlist(n)
+ to_match = random.sample(kwlist,
+ random.randint(1, len(kwlist) - 1))
+ no_match = set(kwlist) - set(to_match)
+ rex = re.compile(regex_opt(to_match))
+ assert rex.groups == 1
+ for w in to_match:
+ assert rex.match(w)
+ for w in no_match:
+ assert not rex.match(w)
+
+
+def test_prefix():
+ opt = regex_opt(('a', 'b'), prefix=r':{1,2}')
+ print(opt)
+ rex = re.compile(opt)
+ assert not rex.match('a')
+ assert rex.match('::a')
+ assert not rex.match(':::') # fullmatch
+
+
+def test_suffix():
+ opt = regex_opt(('a', 'b'), suffix=r':{1,2}')
+ print(opt)
+ rex = re.compile(opt)
+ assert not rex.match('a')
+ assert rex.match('a::')
+ assert not rex.match(':::') # fullmatch
+
+
+def test_suffix_opt():
+ # test that detected suffixes remain sorted.
+ opt = regex_opt(('afoo', 'abfoo'))
+ print(opt)
+ rex = re.compile(opt)
+ m = rex.match('abfoo')
+ assert m.end() == 5
+
+
+def test_different_length_grouping():
+ opt = regex_opt(('a', 'xyz'))
+ print(opt)
+ rex = re.compile(opt)
+ assert rex.match('a')
+ assert rex.match('xyz')
+ assert not rex.match('b')
+ assert rex.groups == 1
+
+
+def test_same_length_grouping():
+ opt = regex_opt(('a', 'b'))
+ print(opt)
+ rex = re.compile(opt)
+ assert rex.match('a')
+ assert rex.match('b')
+ assert not rex.match('x')
+
+ assert rex.groups == 1
+ groups = rex.match('a').groups()
+ assert groups == ('a',)
+
+
+def test_same_length_suffix_grouping():
+ opt = regex_opt(('a', 'b'), suffix='(m)')
+ print(opt)
+ rex = re.compile(opt)
+ assert rex.match('am')
+ assert rex.match('bm')
+ assert not rex.match('xm')
+ assert not rex.match('ax')
+ assert rex.groups == 2
+ groups = rex.match('am').groups()
+ assert groups == ('a', 'm')
diff --git a/tests/test_robotframework_lexer.py b/tests/test_robotframework_lexer.py
new file mode 100644
index 0000000..807fbc4
--- /dev/null
+++ b/tests/test_robotframework_lexer.py
@@ -0,0 +1,38 @@
+"""
+ Pygments Robot Framework lexer tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.robotframework import RobotFrameworkLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield RobotFrameworkLexer()
+
+
+def assert_same_text(lexer, text):
+ """Show that lexed text does not remove any content. """
+ tokens = list(lexer.get_tokens_unprocessed(text))
+ output = ''.join(t[2] for t in tokens)
+ assert text == output
+
+
+def test_empty_brackets_after_scalar_variable(lexer):
+ assert_same_text(lexer, '*** Variables ***\n'
+ '${test}[]\n')
+
+
+def test_empty_brackets_after_list_variable(lexer):
+ assert_same_text(lexer, '*** Variables ***\n'
+ '@{test}[]\n')
+
+
+def test_empty_brackets_after_dict_variable(lexer):
+ assert_same_text(lexer, '*** Variables ***\n'
+ '&{test}[]\n')
diff --git a/tests/test_rtf_formatter.py b/tests/test_rtf_formatter.py
new file mode 100644
index 0000000..1f3ee6e
--- /dev/null
+++ b/tests/test_rtf_formatter.py
@@ -0,0 +1,107 @@
+"""
+ Pygments RTF formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from io import StringIO
+
+from pygments.formatters import RtfFormatter
+from pygments.lexers.special import TextLexer
+
+
+foot = (r'\par' '\n' r'}')
+
+
+def _escape(string):
+ return string.replace("\n", r"\n")
+
+
+def _build_message(*args, **kwargs):
+ string = kwargs.get('string', None)
+ t = _escape(kwargs.get('t', ''))
+ expected = _escape(kwargs.get('expected', ''))
+ result = _escape(kwargs.get('result', ''))
+
+ if string is None:
+ string = ("The expected output of '{t}'\n"
+ "\t\tShould be '{expected}'\n"
+ "\t\tActually outputs '{result}'\n"
+ "\t(WARNING: Partial Output of Result!)")
+
+ end = -len(_escape(foot))
+ start = end - len(expected)
+
+ return string.format(t=t,
+ result = result[start:end],
+ expected = expected)
+
+
+def format_rtf(t):
+ tokensource = list(TextLexer().get_tokens(t))
+ fmt = RtfFormatter()
+ buf = StringIO()
+ fmt.format(tokensource, buf)
+ result = buf.getvalue()
+ buf.close()
+ return result
+
+
+def test_rtf_header():
+ t = ''
+ result = format_rtf(t)
+ expected = r'{\rtf1\ansi\uc0'
+ msg = ("RTF documents are expected to start with '{expected}'\n"
+ "\t\tStarts intead with '{result}'\n"
+ "\t(WARNING: Partial Output of Result!)".format(
+ expected=expected,
+ result=result[:len(expected)]))
+ assert result.startswith(expected), msg
+
+
+def test_rtf_footer():
+ t = ''
+ result = format_rtf(t)
+ expected = ''
+ msg = ("RTF documents are expected to end with '{expected}'\n"
+ "\t\tEnds intead with '{result}'\n"
+ "\t(WARNING: Partial Output of Result!)".format(
+ expected=_escape(expected),
+ result=_escape(result[-len(expected):])))
+ assert result.endswith(expected+foot), msg
+
+
+def test_ascii_characters():
+ t = 'a b c d ~'
+ result = format_rtf(t)
+ expected = (r'a b c d ~')
+ msg = _build_message(t=t, result=result, expected=expected)
+ assert result.endswith(expected+foot), msg
+
+
+def test_escape_characters():
+ t = '\\ {{'
+ result = format_rtf(t)
+ expected = r'\\ \{\{'
+ msg = _build_message(t=t, result=result, expected=expected)
+ assert result.endswith(expected+foot), msg
+
+
+def test_single_characters():
+ t = 'â € ¤ каждой'
+ result = format_rtf(t)
+ expected = (r'{\u226} {\u8364} {\u164} '
+ r'{\u1082}{\u1072}{\u1078}{\u1076}{\u1086}{\u1081}')
+ msg = _build_message(t=t, result=result, expected=expected)
+ assert result.endswith(expected+foot), msg
+
+
+def test_double_characters():
+ t = 'က 힣 ↕ ↕︎ 鼖'
+ result = format_rtf(t)
+ expected = (r'{\u4096} {\u55203} {\u8597} '
+ r'{\u8597}{\u65038} {\u55422}{\u56859}')
+ msg = _build_message(t=t, result=result, expected=expected)
+ assert result.endswith(expected+foot), msg
diff --git a/tests/test_ruby.py b/tests/test_ruby.py
new file mode 100644
index 0000000..a272926
--- /dev/null
+++ b/tests/test_ruby.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+"""
+ Basic RubyLexer Test
+ ~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.token import Name
+from pygments.lexers.ruby import RubyLexer
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield RubyLexer()
+
+
+@pytest.mark.parametrize(
+ 'method_name',
+ (
+ # Bare, un-scoped method names
+ 'a', 'A', 'z', 'Z', 'は', '\u0080', '\uffff',
+ 'aは0_', 'はA__9', '\u0080はa0_', '\uffff__99Z',
+
+ # Method names with trailing characters
+ 'aは!', 'はz?', 'はa=',
+
+ # Scoped method names
+ 'self.a', 'String.は_', 'example.AZ09_!',
+
+ # Operator overrides
+ '+', '+@', '-', '-@', '!', '!@', '~', '~@',
+ '*', '**', '/', '%', '&', '^', '`',
+ '<=>', '<', '<<', '<=', '>', '>>', '>=',
+ '==', '!=', '===', '=~', '!~',
+ '[]', '[]=',
+ )
+)
+def test_positive_method_names(lexer, method_name):
+ """Validate positive method name parsing."""
+
+ text = 'def ' + method_name
+ assert list(lexer.get_tokens(text))[-2] == (Name.Function, method_name.rpartition('.')[2])
+
+
+@pytest.mark.parametrize('method_name', ('1', '_', '<>', '<<=', '>>=', '&&', '||', '==?', '==!', '===='))
+def test_negative_method_names(lexer, method_name):
+ """Validate negative method name parsing."""
+
+ text = 'def ' + method_name
+ assert list(lexer.get_tokens(text))[-2] != (Name.Function, method_name)
diff --git a/tests/test_sql.py b/tests/test_sql.py
new file mode 100644
index 0000000..e0a20f9
--- /dev/null
+++ b/tests/test_sql.py
@@ -0,0 +1,115 @@
+"""
+ Pygments SQL lexers tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.sql import name_between_bracket_re, \
+ name_between_backtick_re, tsql_go_re, tsql_declare_re, \
+ tsql_variable_re, MySqlLexer, TransactSqlLexer
+
+from pygments.token import Comment, Name, Number, Punctuation, Whitespace
+
+
+@pytest.fixture(scope='module')
+def lexer():
+ yield TransactSqlLexer()
+
+
+def _assert_are_tokens_of_type(lexer, examples, expected_token_type):
+ for test_number, example in enumerate(examples.split(), 1):
+ token_count = 0
+ for token_type, token_value in lexer.get_tokens(example):
+ if token_type != Whitespace:
+ token_count += 1
+ assert token_type == expected_token_type, \
+ 'token_type #%d for %s is be %s but must be %s' % \
+ (test_number, token_value, token_type, expected_token_type)
+ assert token_count == 1, \
+ '%s must yield exactly 1 token instead of %d' % \
+ (example, token_count)
+
+
+def _assert_tokens_match(lexer, text, expected_tokens_without_trailing_newline):
+ actual_tokens = tuple(lexer.get_tokens(text))
+ if (len(actual_tokens) >= 1) and (actual_tokens[-1] == (Whitespace, '\n')):
+ actual_tokens = tuple(actual_tokens[:-1])
+ assert expected_tokens_without_trailing_newline == actual_tokens, \
+ 'text must yield expected tokens: %s' % text
+
+
+def test_can_lex_float(lexer):
+ _assert_are_tokens_of_type(lexer,
+ '1. 1.e1 .1 1.2 1.2e3 1.2e+3 1.2e-3 1e2',
+ Number.Float)
+ _assert_tokens_match(lexer,
+ '1e2.1e2',
+ ((Number.Float, '1e2'), (Number.Float, '.1e2')))
+
+
+def test_can_reject_almost_float(lexer):
+ _assert_tokens_match(lexer, '.e1', ((Punctuation, '.'), (Name, 'e1')))
+
+
+def test_can_lex_integer(lexer):
+ _assert_are_tokens_of_type(lexer, '1 23 456', Number.Integer)
+
+
+def test_can_lex_names(lexer):
+ _assert_are_tokens_of_type(
+ lexer,
+ 'thingy thingy123 _thingy _ _123 Ähnliches Müll #temp1 ##temp2',
+ Name)
+
+
+def test_can_lex_comments(lexer):
+ _assert_tokens_match(lexer, '--\n', ((Comment.Single, '--\n'),))
+ _assert_tokens_match(lexer, '/**/', (
+ (Comment.Multiline, '/*'), (Comment.Multiline, '*/')
+ ))
+ _assert_tokens_match(lexer, '/*/**/*/', (
+ (Comment.Multiline, '/*'),
+ (Comment.Multiline, '/*'),
+ (Comment.Multiline, '*/'),
+ (Comment.Multiline, '*/'),
+ ))
+
+
+def test_can_match_analyze_text_res():
+ assert ['`a`', '`bc`'] == \
+ name_between_backtick_re.findall('select `a`, `bc` from some')
+ assert ['[a]', '[bc]'] == \
+ name_between_bracket_re.findall('select [a], [bc] from some')
+ assert tsql_declare_re.search('--\nDeClaRe @some int;')
+ assert tsql_go_re.search('select 1\ngo\n--')
+ assert tsql_variable_re.search('create procedure dbo.usp_x @a int, @b int')
+
+
+def test_can_analyze_text():
+ mysql_lexer = MySqlLexer()
+ tsql_lexer = TransactSqlLexer()
+ code_to_expected_lexer_map = {
+ 'select `a`, `bc` from some': mysql_lexer,
+ 'select [a], [bc] from some': tsql_lexer,
+ '-- `a`, `bc`\nselect [a], [bc] from some': tsql_lexer,
+ '-- `a`, `bc`\nselect [a], [bc] from some; go': tsql_lexer,
+ }
+ sql_lexers = set(code_to_expected_lexer_map.values())
+ for code, expected_lexer in code_to_expected_lexer_map.items():
+ ratings_and_lexers = list((lexer.analyse_text(code), lexer.name) for lexer in sql_lexers)
+ best_rating, best_lexer_name = sorted(ratings_and_lexers, reverse=True)[0]
+ expected_rating = expected_lexer.analyse_text(code)
+ message = (
+ 'lexer must be %s (rating %.2f) instead of '
+ '%s (rating %.2f) for analyse_text() on code:\n%s') % (
+ expected_lexer.name,
+ expected_rating,
+ best_lexer_name,
+ best_rating,
+ code
+ )
+ assert expected_lexer.name == best_lexer_name, message
diff --git a/tests/test_templates.py b/tests/test_templates.py
new file mode 100644
index 0000000..9ed816f
--- /dev/null
+++ b/tests/test_templates.py
@@ -0,0 +1,130 @@
+import pytest
+
+from pygments.lexers.templates import JavascriptDjangoLexer, MasonLexer, \
+ SqlJinjaLexer, VelocityLexer
+
+from pygments.token import Comment
+
+
+@pytest.fixture(scope="module")
+def lexer():
+ yield JavascriptDjangoLexer()
+
+@pytest.fixture(scope='module')
+def lexerMason():
+ yield MasonLexer()
+
+@pytest.fixture(scope='module')
+def lexerVelocity():
+ yield VelocityLexer()
+
+@pytest.fixture(scope='module')
+def lexerSqlJinja():
+ yield SqlJinjaLexer()
+
+def test_do_not_mistake_JSDoc_for_django_comment(lexer):
+ """
+ Test to make sure the lexer doesn't mistake
+ {* ... *} to be a django comment
+ """
+ text = """/**
+ * @param {*} cool
+ */
+ func = function(cool) {
+ };
+
+ /**
+ * @param {*} stuff
+ */
+ fun = function(stuff) {
+ };"""
+ tokens = lexer.get_tokens(text)
+ assert not any(t[0] == Comment for t in tokens)
+
+def test_mason_unnamed_block(lexerMason):
+ text = """
+ <%class>
+ has 'foo';
+ has 'bar' => (required => 1);
+ has 'baz' => (isa => 'Int', default => 17);
+ </%class>
+ """
+ res = lexerMason.analyse_text(text)
+ assert res == 1.0
+
+def test_velocity_macro(lexerVelocity):
+ text = """
+ #macro(getBookListLink, $readingTrackerResult)
+ $readingTrackerResult.getBookListLink()
+ #end
+ """
+ res = lexerVelocity.analyse_text(text)
+ assert res == 0.26
+
+def test_velocity_foreach(lexerVelocity):
+ text = """
+ <ul>
+ #foreach( $product in $allProducts )
+ <li>$product</li>
+ #end
+ </ul>
+ """
+ res = lexerVelocity.analyse_text(text)
+ assert res == 0.16
+
+def test_velocity_if(lexerVelocity):
+ text = """
+ #if( $display )
+ <strong>Velocity!</strong>
+ #end
+ """
+ res = lexerVelocity.analyse_text(text)
+ assert res == 0.16
+
+def test_velocity_reference(lexerVelocity):
+ text = """
+ Hello $name! Welcome to Velocity!
+ """
+ res = lexerVelocity.analyse_text(text)
+ assert res == 0.01
+
+def test_sql_jinja_dbt_ref(lexerSqlJinja):
+ text = """
+ {%- set payment_methods = ["bank_transfer", "credit_card", "gift_card"] -%}
+
+ select
+ order_id,
+ {%- for payment_method in payment_methods %}
+ sum(case when payment_method = '{{payment_method}}' then amount end) as {{payment_method}}_amount
+ {%- if not loop.last %},{% endif -%}
+ {% endfor %}
+ from {{ ref('raw_payments') }}
+ group by 1
+ """
+ res = lexerSqlJinja.analyse_text(text)
+ assert res == 0.4
+
+def test_sql_jinja_dbt_source(lexerSqlJinja):
+ text = """
+ {%- set payment_methods = ["bank_transfer", "credit_card", "gift_card"] -%}
+
+ select
+ order_id,
+ {%- for payment_method in payment_methods %}
+ sum(case when payment_method = '{{payment_method}}' then amount end) as {{payment_method}}_amount
+ {%- if not loop.last %},{% endif -%}
+ {% endfor %}
+ from {{ source('payments_db', 'payments') }}
+ group by 1
+ """
+ res = lexerSqlJinja.analyse_text(text)
+ assert res == 0.25
+
+def test_sql_jinja_dbt_macro(lexerSqlJinja):
+ text = """
+ {% macro cents_to_dollars(column_name, precision=2) %}
+ ({{ column_name }} / 100)::numeric(16, {{ precision }})
+ {% endmacro %}
+ """
+ res = lexerSqlJinja.analyse_text(text)
+ assert res == 0.15
diff --git a/tests/test_terminal_formatter.py b/tests/test_terminal_formatter.py
new file mode 100644
index 0000000..c1a7ec7
--- /dev/null
+++ b/tests/test_terminal_formatter.py
@@ -0,0 +1,100 @@
+"""
+ Pygments terminal formatter tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+from io import StringIO
+
+from pygments.lexers.sql import PlPgsqlLexer
+from pygments.formatters import TerminalFormatter, Terminal256Formatter, \
+ HtmlFormatter, LatexFormatter
+
+from pygments.style import Style
+from pygments.token import Token
+from pygments.lexers import Python3Lexer
+from pygments import highlight
+
+DEMO_TEXT = '''\
+-- comment
+select
+* from bar;
+'''
+DEMO_LEXER = PlPgsqlLexer
+DEMO_TOKENS = list(DEMO_LEXER().get_tokens(DEMO_TEXT))
+
+ANSI_RE = re.compile(r'\x1b[\w\W]*?m')
+
+
+def strip_ansi(x):
+ return ANSI_RE.sub('', x)
+
+
+def test_reasonable_output():
+ out = StringIO()
+ TerminalFormatter().format(DEMO_TOKENS, out)
+ plain = strip_ansi(out.getvalue())
+ assert DEMO_TEXT.count('\n') == plain.count('\n')
+ print(repr(plain))
+
+ for a, b in zip(DEMO_TEXT.splitlines(), plain.splitlines()):
+ assert a == b
+
+
+def test_reasonable_output_lineno():
+ out = StringIO()
+ TerminalFormatter(linenos=True).format(DEMO_TOKENS, out)
+ plain = strip_ansi(out.getvalue())
+ assert DEMO_TEXT.count('\n') + 1 == plain.count('\n')
+ print(repr(plain))
+
+ for a, b in zip(DEMO_TEXT.splitlines(), plain.splitlines()):
+ assert a in b
+
+
+class MyStyle(Style):
+ styles = {
+ Token.Comment: 'ansibrightblack',
+ Token.String: 'ansibrightblue bg:ansired',
+ Token.Number: 'ansibrightgreen bg:ansigreen',
+ Token.Number.Hex: 'ansigreen bg:ansibrightred',
+ }
+
+
+CODE = '''
+# this should be a comment
+print("Hello World")
+async def function(a,b,c, *d, **kwarg:Bool)->Bool:
+ pass
+ return 123, 0xb3e3
+
+'''
+
+
+def test_style_html():
+ style = HtmlFormatter(style=MyStyle).get_style_defs()
+ assert '#555555' in style, "ansigray for comment not html css style"
+
+
+def test_others_work():
+ """Check other formatters don't crash."""
+ highlight(CODE, Python3Lexer(), LatexFormatter(style=MyStyle))
+ highlight(CODE, Python3Lexer(), HtmlFormatter(style=MyStyle))
+
+
+def test_256esc_seq():
+ """
+ Test that a few escape sequences are actually used when using ansi<> color
+ codes.
+ """
+ def termtest(x):
+ return highlight(x, Python3Lexer(),
+ Terminal256Formatter(style=MyStyle))
+
+ assert '32;101' in termtest('0x123')
+ assert '92;42' in termtest('123')
+ assert '90' in termtest('#comment')
+ assert '94;41' in termtest('"String"')
diff --git a/tests/test_thingsdb.py b/tests/test_thingsdb.py
new file mode 100644
index 0000000..25e3816
--- /dev/null
+++ b/tests/test_thingsdb.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+ Basic ThingsDB Test
+ ~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import unittest
+
+from pygments.token import Number, Text, Comment
+from pygments.lexers import ThingsDBLexer
+
+
+class ThingsDBTest(unittest.TestCase):
+
+ def setUp(self):
+ self.lexer = ThingsDBLexer()
+ self.maxDiff = None
+
+ def testNumber(self):
+ fragment = u'42'
+ tokens = [
+ (Number.Integer, u'42'),
+ (Text.Whitespace, '\n')
+ ]
+ self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
+
+ def testThingId(self):
+ fragment = u'#42'
+ tokens = [
+ (Comment.Preproc, u'#42'),
+ (Text.Whitespace, '\n')
+ ]
+ self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
diff --git a/tests/test_tnt.py b/tests/test_tnt.py
new file mode 100644
index 0000000..c2282ac
--- /dev/null
+++ b/tests/test_tnt.py
@@ -0,0 +1,226 @@
+"""
+ Typograhic Number Theory tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import pytest
+
+from pygments.lexers.tnt import TNTLexer
+from pygments.token import Text, Operator, Keyword, Name, Number, \
+ Punctuation, Error
+
+
+@pytest.fixture(autouse=True)
+def lexer():
+ yield TNTLexer()
+
+
+# whitespace
+
+@pytest.mark.parametrize('text', (' a', ' \t0', '\n\n 3'))
+def test_whitespace_positive_matches(lexer, text):
+ """Test fragments that should be tokenized as whitespace text."""
+ assert lexer.whitespace(0, text) == len(text) - 1
+ assert lexer.whitespace(0, text, True) == len(text) - 1
+ assert lexer.cur[-1] == (0, Text, text[:-1])
+
+
+@pytest.mark.parametrize('text', ('0 a=b premise', 'b=a symmetry'))
+def test_whitespace_negative_matches(lexer, text):
+ """Test statements that do not start with whitespace text."""
+ assert lexer.whitespace(0, text) == 0
+ with pytest.raises(AssertionError):
+ lexer.whitespace(0, text, True)
+ assert not lexer.cur
+
+
+# terms that can go on either side of an = sign
+
+@pytest.mark.parametrize('text', ('a ', "a' ", 'b ', "c' "))
+def test_variable_positive_matches(lexer, text):
+ """Test fragments that should be tokenized as variables."""
+ assert lexer.variable(0, text) == len(text) - 1
+ assert lexer.cur[-1] == (0, Name.Variable, text[:-1])
+
+
+@pytest.mark.parametrize('text', ("' ", 'f ', "f' "))
+def test_variable_negative_matches(lexer, text):
+ """Test fragments that should **not** be tokenized as variables."""
+ with pytest.raises(AssertionError):
+ lexer.variable(0, text)
+ assert not lexer.cur
+
+
+@pytest.mark.parametrize('text', ('0', 'S0', 'SSSSS0'))
+def test_numeral_positive_matches(lexer, text):
+ """Test fragments that should be tokenized as (unary) numerals."""
+ assert lexer.term(0, text) == len(text)
+ assert lexer.cur[-1] == (len(text) - 1, Number.Integer, text[-1])
+ if text != '0':
+ assert lexer.cur[-2] == (0, Number.Integer, text[:-1])
+
+
+@pytest.mark.parametrize('text', (
+ '(a+b)', '(b.a)', '(c+d)'
+))
+def test_multiterm_positive_matches(lexer, text):
+ """Test fragments that should be tokenized as a compound term."""
+ assert lexer.term(0, text) == len(text)
+ assert [t[1] for t in lexer.cur] == [
+ Punctuation, Name.Variable, Operator,
+ Name.Variable, Punctuation
+ ]
+
+
+@pytest.mark.parametrize('text', ('1', '=', 'A'))
+def test_term_negative_matches(lexer, text):
+ """Test fragments that should not be tokenized as terms at all."""
+ with pytest.raises(AssertionError):
+ lexer.term(0, text)
+ assert not lexer.cur
+
+
+# full statements, minus rule
+
+@pytest.mark.parametrize('text', ('~a=b ', '~~~~a=b '))
+def test_negator_positive_matches(lexer, text):
+ """Test statements that start with a negation."""
+ assert lexer.formula(0, text) == len(text) - 1
+ assert lexer.cur[0] == (0, Operator, text[:-4])
+
+
+@pytest.mark.parametrize('text', ('Aa:a=b ', 'Eb:a=b '))
+def test_quantifier_positive_matches(lexer, text):
+ """Test statements that start with a quantifier."""
+ assert lexer.formula(0, text) == len(text) - 1
+ assert lexer.cur[0][1] == Keyword.Declaration
+ assert lexer.cur[1][1] == Name.Variable
+ assert lexer.cur[2] == (2, Punctuation, ':')
+
+
+@pytest.mark.parametrize('text', ('Aaa=b', 'Eba=b'))
+def test_quantifier_negative_matches(lexer, text):
+ """Test quantifiers that are only partially valid."""
+ with pytest.raises(AssertionError):
+ lexer.formula(0, text)
+ # leftovers should still be valid
+ assert lexer.cur[0][1] == Keyword.Declaration
+ assert lexer.cur[1][1] == Name.Variable
+
+
+@pytest.mark.parametrize('text', ('<a=b&b=a>', '<a=b|b=a>', '<a=b]b=a>'))
+def test_compound_positive_matches(lexer, text):
+ """Test statements that consist of multiple formulas compounded."""
+ assert lexer.formula(0, text) == len(text)
+ assert lexer.cur[0] == (0, Punctuation, '<')
+ assert lexer.cur[4][1] == Operator
+ assert lexer.cur[-1] == (len(text)-1, Punctuation, '>')
+
+
+@pytest.mark.parametrize('text', ('<a=b/b=a>', '<a=b&b=a '))
+def test_compound_negative_matches(lexer, text):
+ """Test statements that look like compounds but are invalid."""
+ with pytest.raises(AssertionError):
+ lexer.formula(0, text)
+ assert lexer.cur[0] == (0, Punctuation, '<')
+
+
+@pytest.mark.parametrize('text', ('a=b ', 'a=0 ', '0=b '))
+def test_formula_postive_matches(lexer, text):
+ """Test the normal singular formula."""
+ assert lexer.formula(0, text) == len(text) - 1
+ assert lexer.cur[0][2] == text[0]
+ assert lexer.cur[1] == (1, Operator, '=')
+ assert lexer.cur[2][2] == text[2]
+
+
+@pytest.mark.parametrize('text', ('a/b', '0+0 '))
+def test_formula_negative_matches(lexer, text):
+ """Test anything but an equals sign."""
+ with pytest.raises(AssertionError):
+ lexer.formula(0, text)
+
+
+# rules themselves
+
+@pytest.mark.parametrize('text', (
+ 'fantasy rule', 'carry over line 5', 'premise', 'joining',
+ 'double-tilde', 'switcheroo', 'De Morgan', 'specification'
+))
+def test_rule_positive_matches(lexer, text):
+ """Test some valid rules of TNT."""
+ assert lexer.rule(0, text) == len(text)
+ assert lexer.cur[0][:2] == (0, Keyword)
+ if text[-1].isdigit():
+ assert lexer.cur[1][1] == Number.Integer
+
+
+@pytest.mark.parametrize('text', (
+ 'fantasy', 'carry over', 'premse', 'unjoining',
+ 'triple-tilde', 'switcheru', 'De-Morgan', 'despecification'
+))
+def test_rule_negative_matches(lexer, text):
+ """Test some invalid rules of TNT."""
+ with pytest.raises(AssertionError):
+ lexer.rule(0, text)
+
+
+# referrals
+
+@pytest.mark.parametrize('text', ('(lines 1, 2, and 4)', '(line 3,5,6)',
+ '(lines 1, 6 and 0)'))
+def test_lineno_positive_matches(lexer, text):
+ """Test line referrals."""
+ assert lexer.lineno(0, text) == len(text)
+ assert lexer.cur[0] == (0, Punctuation, '(')
+ assert lexer.cur[1][:2] == (1, Text)
+ assert lexer.cur[2][1] == Number.Integer
+ assert lexer.cur[3] == (len(text)-1, Punctuation, ')')
+
+
+@pytest.mark.parametrize('text', (
+ '(lines one, two, and four)1 ', # to avoid IndexError
+ '(lines 1 2 and 3)', '(lines 1 2 3)'
+))
+def test_lineno_negative_matches(lexer, text):
+ """Test invalid line referrals."""
+ with pytest.raises(AssertionError):
+ lexer.lineno(0, text)
+
+
+# worst-case: error text
+
+@pytest.mark.parametrize('text', ('asdf', 'fdsa\nasdf', 'asdf\n '))
+def test_error_till_line_end(lexer, text):
+ try:
+ nl = text.index('\n')
+ except ValueError:
+ nl = len(text)
+ try:
+ end = text.find(text.split(None, 2)[1])
+ except IndexError: # split failed
+ end = len(text)
+ assert lexer.error_till_line_end(0, text) == end
+ assert lexer.cur[0] == (0, Error, text[:nl])
+
+
+# full statement, including rule (because this can't be tested any other way)
+
+@pytest.mark.parametrize('text', ('[ push', '] pop'))
+def test_fantasy_positive_matches(lexer, text):
+ """Test statements that should be tokenized as push/pop statements."""
+ assert lexer.get_tokens_unprocessed(text)[0] == (0, Keyword, text[0])
+
+
+# full text is already done by examplefiles, but here's some exceptions
+
+@pytest.mark.parametrize('text', (
+ '0', 'a=b', 'premise',
+ '0 a=b premise', '1 b=a symmetry (line 0)'
+))
+def test_no_crashing(lexer, text):
+ """Test incomplete text fragments that shouldn't crash the whole lexer."""
+ assert lexer.get_tokens(text)
diff --git a/tests/test_token.py b/tests/test_token.py
new file mode 100644
index 0000000..c155475
--- /dev/null
+++ b/tests/test_token.py
@@ -0,0 +1,51 @@
+"""
+ Test suite for the token module
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import copy
+
+import pytest
+
+from pygments import token
+
+
+def test_tokentype():
+ t = token.String
+ assert t.split() == [token.Token, token.Literal, token.String]
+ assert t.__class__ is token._TokenType
+
+
+def test_functions():
+ assert token.is_token_subtype(token.String, token.String)
+ assert token.is_token_subtype(token.String, token.Literal)
+ assert not token.is_token_subtype(token.Literal, token.String)
+
+ assert token.string_to_tokentype(token.String) is token.String
+ assert token.string_to_tokentype('') is token.Token
+ assert token.string_to_tokentype('String') is token.String
+
+
+def test_sanity_check():
+ stp = token.STANDARD_TYPES.copy()
+ stp[token.Token] = '---' # Token and Text do conflict, that is okay
+ t = {}
+ for k, v in stp.items():
+ t.setdefault(v, []).append(k)
+ if len(t) == len(stp):
+ return # Okay
+
+ for k, v in t.items():
+ if len(v) > 1:
+ pytest.fail("%r has more than one key: %r" % (k, v))
+
+
+def test_copying():
+ # Token instances are supposed to be singletons, so copying or even
+ # deepcopying should return themselves
+ t = token.String
+ assert t is copy.copy(t)
+ assert t is copy.deepcopy(t)
diff --git a/tests/test_unistring.py b/tests/test_unistring.py
new file mode 100644
index 0000000..65fb1fc
--- /dev/null
+++ b/tests/test_unistring.py
@@ -0,0 +1,45 @@
+"""
+ Test suite for the unistring module
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+import random
+
+from pygments import unistring as uni
+
+
+def test_cats_exist_and_compilable():
+ for cat in uni.cats:
+ s = getattr(uni, cat)
+ if s == '': # Probably Cs on Jython
+ continue
+ print("%s %r" % (cat, s))
+ re.compile('[%s]' % s)
+
+
+def _cats_that_match(c):
+ matching_cats = []
+ for cat in uni.cats:
+ s = getattr(uni, cat)
+ if s == '': # Probably Cs on Jython
+ continue
+ if re.compile('[%s]' % s).match(c):
+ matching_cats.append(cat)
+ return matching_cats
+
+
+def test_spot_check_types():
+ # Each char should match one, and precisely one, category
+ random.seed(0)
+ for i in range(1000):
+ o = random.randint(0, 65535)
+ c = chr(o)
+ if o > 0xd800 and o <= 0xdfff and not uni.Cs:
+ continue # Bah, Jython.
+ print(hex(o))
+ cats = _cats_that_match(c)
+ assert len(cats) == 1, "%d (%s): %s" % (o, c, cats)
diff --git a/tests/test_usd.py b/tests/test_usd.py
new file mode 100755
index 0000000..5438983
--- /dev/null
+++ b/tests/test_usd.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+"""Test that syntax highlighting for USD files works correctly."""
+
+import textwrap
+import unittest
+
+from pygments.lexers import UsdLexer
+from pygments.token import Name, String, Whitespace
+
+
+class _Common(unittest.TestCase):
+ """A basic class that makes it easier to write unittests."""
+
+ def setUp(self):
+ """Create a fresh USD lexer class before each test runs."""
+ self.lexer = UsdLexer()
+
+ def _get(self, code):
+ """Tokenize the code into its unique parts.
+
+ :param code: The USD source code to split up.
+ :type code: str
+
+ :returns: The tokenized pieces.
+ :rtype: list[:class:`pygments.token._TokenType`]
+
+ """
+ return list(self.lexer.get_tokens(code))
+
+
+class Features(_Common):
+ """Test that different features of USD highlight as expected."""
+
+ def test_asset_path(self):
+ """Check that a regular file path highlights correctly."""
+ for path in [
+ "@./some/path/to/a/file/foo.usda@",
+ "@/some/path/to/a/file/foo.usda@",
+ "@some/path/to/a/file/foo.usda@",
+ r"@file://SPECI__Z-_ALIZED(syntax_here)?with_arbitrary#)(%*&)\characters.tar.gz@",
+ ]:
+ expected = [
+ (String.Interpol, path),
+ (Whitespace, "\n"),
+ ]
+
+ self.assertEqual(expected, self._get(path))
+
+ def test_target_absolute(self):
+ """Check that SdfPath syntax examples work correctly."""
+ for code in [
+ # Absolute paths
+ "</some/another_one/here>",
+ "</some/path/here.property_name>",
+ "</some/path/here>",
+ # Relative paths
+ "<../some/another_one/here>",
+ "<../some/path/here.property_name>",
+ "<../some/path/here>",
+ ]:
+ self.assertEqual(
+ [(Name.Namespace, code), (Whitespace, "\n")], self._get(code),
+ )
diff --git a/tests/test_using_api.py b/tests/test_using_api.py
new file mode 100644
index 0000000..7b0b030
--- /dev/null
+++ b/tests/test_using_api.py
@@ -0,0 +1,39 @@
+"""
+ Pygments tests for using()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pytest import raises
+
+from pygments.lexer import using, bygroups, this, RegexLexer
+from pygments.token import String, Text, Keyword
+
+
+class MyLexer(RegexLexer):
+ tokens = {
+ 'root': [
+ (r'#.*',
+ using(this, state='invalid')),
+ (r'(")(.+?)(")',
+ bygroups(String, using(this, state='string'), String)),
+ (r'[^"]+', Text),
+ ],
+ 'string': [
+ (r'.+', Keyword),
+ ],
+ }
+
+
+def test_basic():
+ expected = [(Text, 'a'), (String, '"'), (Keyword, 'bcd'),
+ (String, '"'), (Text, 'e\n')]
+ assert list(MyLexer().get_tokens('a"bcd"e')) == expected
+
+
+def test_error():
+ def gen():
+ return list(MyLexer().get_tokens('#a'))
+ assert raises(KeyError, gen)
diff --git a/tests/test_util.py b/tests/test_util.py
new file mode 100644
index 0000000..d140836
--- /dev/null
+++ b/tests/test_util.py
@@ -0,0 +1,189 @@
+"""
+ Test suite for the util module
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pytest import raises
+
+from pygments import util, console
+
+
+class FakeLexer:
+ def analyse(text):
+ return text
+ analyse = util.make_analysator(analyse)
+
+
+def test_getoptions():
+ assert util.get_bool_opt({}, 'a', True) is True
+ assert util.get_bool_opt({}, 'a', 1) is True
+ assert util.get_bool_opt({}, 'a', 'true') is True
+ assert util.get_bool_opt({}, 'a', 'no') is False
+ assert raises(util.OptionError, util.get_bool_opt, {}, 'a', [])
+ assert raises(util.OptionError, util.get_bool_opt, {}, 'a', 'foo')
+
+ assert util.get_int_opt({}, 'a', 1) == 1
+ assert raises(util.OptionError, util.get_int_opt, {}, 'a', [])
+ assert raises(util.OptionError, util.get_int_opt, {}, 'a', 'bar')
+
+ assert util.get_list_opt({}, 'a', [1]) == [1]
+ assert util.get_list_opt({}, 'a', '1 2') == ['1', '2']
+ assert raises(util.OptionError, util.get_list_opt, {}, 'a', 1)
+
+ assert util.get_choice_opt({}, 'a', ['foo', 'bar'], 'bar') == 'bar'
+ assert util.get_choice_opt({}, 'a', ['foo', 'bar'], 'Bar', True) == 'bar'
+ assert raises(util.OptionError, util.get_choice_opt, {}, 'a',
+ ['foo', 'bar'], 'baz')
+
+
+def test_docstring_headline():
+ def f1():
+ """
+ docstring headline
+
+ other text
+ """
+ def f2():
+ """
+ docstring
+ headline
+
+ other text
+ """
+ def f3():
+ pass
+
+ assert util.docstring_headline(f1) == 'docstring headline'
+ assert util.docstring_headline(f2) == 'docstring headline'
+ assert util.docstring_headline(f3) == ''
+
+
+def test_analysator_returns_float():
+ # If an analysator wrapped by make_analysator returns a floating point
+ # number, then that number will be returned by the wrapper.
+ assert FakeLexer.analyse('0.5') == 0.5
+
+
+def test_analysator_returns_boolean():
+ # If an analysator wrapped by make_analysator returns a boolean value,
+ # then the wrapper will return 1.0 if the boolean was True or 0.0 if
+ # it was False.
+ assert FakeLexer.analyse(True) == 1.0
+ assert FakeLexer.analyse(False) == 0.0
+
+
+def test_analysator_raises_exception():
+ # If an analysator wrapped by make_analysator raises an exception,
+ # then the wrapper will return 0.0.
+ class ErrorLexer:
+ def analyse(text):
+ raise RuntimeError('something bad happened')
+ analyse = util.make_analysator(analyse)
+ assert ErrorLexer.analyse('') == 0.0
+
+
+def test_analysator_value_error():
+ # When converting the analysator's return value to a float a
+ # ValueError may occur. If that happens 0.0 is returned instead.
+ assert FakeLexer.analyse('bad input') == 0.0
+
+
+def test_analysator_type_error():
+ # When converting the analysator's return value to a float a
+ # TypeError may occur. If that happens 0.0 is returned instead.
+ assert FakeLexer.analyse('xxx') == 0.0
+
+
+def test_shebang_matches():
+ assert util.shebang_matches('#!/usr/bin/env python\n', r'python(2\.\d)?')
+ assert util.shebang_matches('#!/usr/bin/python2.4', r'python(2\.\d)?')
+ assert util.shebang_matches('#!/usr/bin/startsomethingwith python',
+ r'python(2\.\d)?')
+ assert util.shebang_matches('#!C:\\Python2.4\\Python.exe', r'python(2\.\d)?')
+
+ assert not util.shebang_matches('#!/usr/bin/python-ruby', r'python(2\.\d)?')
+ assert not util.shebang_matches('#!/usr/bin/python/ruby', r'python(2\.\d)?')
+ assert not util.shebang_matches('#!', r'python')
+
+
+def test_doctype_matches():
+ assert util.doctype_matches('<!DOCTYPE html> <html>', 'html.*')
+ assert not util.doctype_matches(
+ '<?xml ?> <DOCTYPE html PUBLIC "a"> <html>', 'html.*')
+ assert util.html_doctype_matches(
+ '<?xml ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">')
+
+
+def test_xml():
+ assert util.looks_like_xml(
+ '<?xml ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">')
+ assert util.looks_like_xml('<html xmlns>abc</html>')
+ assert not util.looks_like_xml('<html>')
+
+
+def test_format_lines():
+ lst = ['cat', 'dog']
+ output = util.format_lines('var', lst)
+ d = {}
+ exec(output, d)
+ assert isinstance(d['var'], tuple)
+ assert ('cat', 'dog') == d['var']
+
+
+def test_duplicates_removed_seq_types():
+ # tuple
+ x = util.duplicates_removed(('a', 'a', 'b'))
+ assert ['a', 'b'] == x
+ # list
+ x = util.duplicates_removed(['a', 'a', 'b'])
+ assert ['a', 'b'] == x
+ # iterator
+ x = util.duplicates_removed(iter(('a', 'a', 'b')))
+ assert ['a', 'b'] == x
+
+
+def test_duplicates_removed_nonconsecutive():
+ # keeps first
+ x = util.duplicates_removed(('a', 'b', 'a'))
+ assert ['a', 'b'] == x
+
+
+def test_guess_decode():
+ # UTF-8 should be decoded as UTF-8
+ s = util.guess_decode('\xff'.encode())
+ assert s == ('\xff', 'utf-8')
+
+ # otherwise, it could be latin1 or the locale encoding...
+ import locale
+ s = util.guess_decode(b'\xff')
+ assert s[1] in ('latin1', locale.getpreferredencoding())
+
+
+def test_guess_decode_from_terminal():
+ class Term:
+ encoding = 'utf-7'
+
+ s = util.guess_decode_from_terminal('\xff'.encode('utf-7'), Term)
+ assert s == ('\xff', 'utf-7')
+
+ s = util.guess_decode_from_terminal('\xff'.encode(), Term)
+ assert s == ('\xff', 'utf-8')
+
+
+def test_console_ansiformat():
+ f = console.ansiformat
+ c = console.codes
+ all_attrs = f('+*_blue_*+', 'text')
+ assert c['blue'] in all_attrs and c['blink'] in all_attrs
+ assert c['bold'] in all_attrs and c['underline'] in all_attrs
+ assert c['reset'] in all_attrs
+ assert raises(KeyError, f, '*mauve*', 'text')
+
+
+def test_console_functions():
+ assert console.reset_color() == console.codes['reset']
+ assert console.colorize('blue', 'text') == \
+ console.codes['blue'] + 'text' + console.codes['reset']
diff --git a/tests/test_words.py b/tests/test_words.py
new file mode 100644
index 0000000..9a8730a
--- /dev/null
+++ b/tests/test_words.py
@@ -0,0 +1,366 @@
+"""
+ Pygments tests for words()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Token
+
+
+class MyLexer(RegexLexer):
+ tokens = {
+ "root": [
+ (
+ words(
+ [
+ "a-word",
+ "another-word",
+ # Test proper escaping of a few things that can occur
+ # in regular expressions. They are all matched literally.
+ "[",
+ "]",
+ "^",
+ "\\",
+ "(",
+ ")",
+ "(?:",
+ "-",
+ "|",
+ r"\w",
+ ]
+ ),
+ Token.Name,
+ ),
+ (words(["space-allowed-before-this"], prefix=" ?"), Token.Name),
+ (words(["space-allowed-after-this"], suffix=" ?"), Token.Name),
+ (
+ words(["space-required-before-and-after-this"], prefix=" ", suffix=" "),
+ Token.Name,
+ ),
+ # prefix and suffix can be regexes.
+ (words(["one-whitespace-allowed-before-this"], prefix=r"\s?"), Token.Name),
+ (words(["all-whitespace-allowed-after-this"], suffix=r"\s*"), Token.Name),
+ (
+ words(
+ ["all-whitespace-allowed-one-required-after-this"], suffix=r"\s+"
+ ),
+ Token.Name,
+ ),
+ (r"\n", Token.Text),
+ ],
+ }
+
+
+def test_basic():
+ s = "a-word this-is-not-in-the-list another-word"
+ assert list(MyLexer().get_tokens(s)) == [
+ (Token.Name, "a-word"),
+ (Token.Error, " "),
+ (Token.Error, "t"),
+ (Token.Error, "h"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Name, "-"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Name, "-"),
+ (Token.Error, "n"),
+ (Token.Error, "o"),
+ (Token.Error, "t"),
+ (Token.Name, "-"),
+ (Token.Error, "i"),
+ (Token.Error, "n"),
+ (Token.Name, "-"),
+ (Token.Error, "t"),
+ (Token.Error, "h"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "l"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Error, "t"),
+ (Token.Error, " "),
+ (Token.Name, "another-word"),
+ (Token.Text, "\n"),
+ ]
+
+
+def test_special_characters():
+ s = """
+[
+]
+^
+\\
+(
+)
+(?:
+-
+|
+\\w
+"""
+ assert list(MyLexer().get_tokens(s)) == [
+ (Token.Name, "["),
+ (Token.Text, "\n"),
+ (Token.Name, "]"),
+ (Token.Text, "\n"),
+ (Token.Name, "^"),
+ (Token.Text, "\n"),
+ (Token.Name, "\\"),
+ (Token.Text, "\n"),
+ (Token.Name, "("),
+ (Token.Text, "\n"),
+ (Token.Name, ")"),
+ (Token.Text, "\n"),
+ (Token.Name, "(?:"),
+ (Token.Text, "\n"),
+ (Token.Name, "-"),
+ (Token.Text, "\n"),
+ (Token.Name, "|"),
+ (Token.Text, "\n"),
+ (Token.Name, "\\w"),
+ (Token.Text, "\n"),
+ ]
+
+
+def test_affixes():
+ s = """
+space-allowed-after-this |
+space-allowed-before-this
+space-allowed-after-this
+ space-required-before-and-after-this |
+space-required-before-and-after-this |
+ space-required-before-and-after-this<= no space after
+"""
+ assert list(MyLexer().get_tokens(s)) == [
+ (Token.Name, "space-allowed-after-this "),
+ (Token.Name, "|"),
+ (Token.Text, "\n"),
+ (Token.Name, "space-allowed-before-this"),
+ (Token.Text, "\n"),
+ (Token.Name, "space-allowed-after-this"),
+ (Token.Text, "\n"),
+ (Token.Name, " space-required-before-and-after-this "),
+ (Token.Name, "|"),
+ (Token.Text, "\n"),
+ (Token.Error, "s"),
+ (Token.Error, "p"),
+ (Token.Error, "a"),
+ (Token.Error, "c"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "q"),
+ (Token.Error, "u"),
+ (Token.Error, "i"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "b"),
+ (Token.Error, "e"),
+ (Token.Error, "f"),
+ (Token.Error, "o"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "n"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "f"),
+ (Token.Error, "t"),
+ (Token.Error, "e"),
+ (Token.Error, "r"),
+ (Token.Name, "-"),
+ (Token.Error, "t"),
+ (Token.Error, "h"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Error, " "),
+ (Token.Name, "|"),
+ (Token.Text, "\n"),
+ (Token.Error, " "),
+ (Token.Error, "s"),
+ (Token.Error, "p"),
+ (Token.Error, "a"),
+ (Token.Error, "c"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "q"),
+ (Token.Error, "u"),
+ (Token.Error, "i"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "b"),
+ (Token.Error, "e"),
+ (Token.Error, "f"),
+ (Token.Error, "o"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "n"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "f"),
+ (Token.Error, "t"),
+ (Token.Error, "e"),
+ (Token.Error, "r"),
+ (Token.Name, "-"),
+ (Token.Error, "t"),
+ (Token.Error, "h"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Error, "<"),
+ (Token.Error, "="),
+ (Token.Error, " "),
+ (Token.Error, "n"),
+ (Token.Error, "o"),
+ (Token.Error, " "),
+ (Token.Error, "s"),
+ (Token.Error, "p"),
+ (Token.Error, "a"),
+ (Token.Error, "c"),
+ (Token.Error, "e"),
+ (Token.Error, " "),
+ (Token.Error, "a"),
+ (Token.Error, "f"),
+ (Token.Error, "t"),
+ (Token.Error, "e"),
+ (Token.Error, "r"),
+ (Token.Text, "\n"),
+ ]
+
+
+def test_affixes_regexes():
+ s = """
+ one-whitespace-allowed-before-this
+NOT-WHITESPACEone-whitespace-allowed-before-this
+all-whitespace-allowed-after-this \n \t
+all-whitespace-allowed-after-thisNOT-WHITESPACE
+all-whitespace-allowed-one-required-after-thisNOT-WHITESPACE"""
+ assert list(MyLexer().get_tokens(s)) == [
+ (Token.Name, " one-whitespace-allowed-before-this"),
+ (Token.Text, "\n"),
+ (Token.Error, "N"),
+ (Token.Error, "O"),
+ (Token.Error, "T"),
+ (Token.Name, "-"),
+ (Token.Error, "W"),
+ (Token.Error, "H"),
+ (Token.Error, "I"),
+ (Token.Error, "T"),
+ (Token.Error, "E"),
+ (Token.Error, "S"),
+ (Token.Error, "P"),
+ (Token.Error, "A"),
+ (Token.Error, "C"),
+ (Token.Error, "E"),
+ (Token.Name, "one-whitespace-allowed-before-this"),
+ (Token.Text, "\n"),
+ (Token.Name, "all-whitespace-allowed-after-this \n \t\n"),
+ (Token.Name, "all-whitespace-allowed-after-this"),
+ (Token.Error, "N"),
+ (Token.Error, "O"),
+ (Token.Error, "T"),
+ (Token.Name, "-"),
+ (Token.Error, "W"),
+ (Token.Error, "H"),
+ (Token.Error, "I"),
+ (Token.Error, "T"),
+ (Token.Error, "E"),
+ (Token.Error, "S"),
+ (Token.Error, "P"),
+ (Token.Error, "A"),
+ (Token.Error, "C"),
+ (Token.Error, "E"),
+ (Token.Text, "\n"),
+ (Token.Error, "a"),
+ (Token.Error, "l"),
+ (Token.Error, "l"),
+ (Token.Name, "-"),
+ (Token.Error, "w"),
+ (Token.Error, "h"),
+ (Token.Error, "i"),
+ (Token.Error, "t"),
+ (Token.Error, "e"),
+ (Token.Error, "s"),
+ (Token.Error, "p"),
+ (Token.Error, "a"),
+ (Token.Error, "c"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "l"),
+ (Token.Error, "l"),
+ (Token.Error, "o"),
+ (Token.Error, "w"),
+ (Token.Error, "e"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "o"),
+ (Token.Error, "n"),
+ (Token.Error, "e"),
+ (Token.Name, "-"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "q"),
+ (Token.Error, "u"),
+ (Token.Error, "i"),
+ (Token.Error, "r"),
+ (Token.Error, "e"),
+ (Token.Error, "d"),
+ (Token.Name, "-"),
+ (Token.Error, "a"),
+ (Token.Error, "f"),
+ (Token.Error, "t"),
+ (Token.Error, "e"),
+ (Token.Error, "r"),
+ (Token.Name, "-"),
+ (Token.Error, "t"),
+ (Token.Error, "h"),
+ (Token.Error, "i"),
+ (Token.Error, "s"),
+ (Token.Error, "N"),
+ (Token.Error, "O"),
+ (Token.Error, "T"),
+ (Token.Name, "-"),
+ (Token.Error, "W"),
+ (Token.Error, "H"),
+ (Token.Error, "I"),
+ (Token.Error, "T"),
+ (Token.Error, "E"),
+ (Token.Error, "S"),
+ (Token.Error, "P"),
+ (Token.Error, "A"),
+ (Token.Error, "C"),
+ (Token.Error, "E"),
+ (Token.Text, "\n"),
+ ]
+
+
+class MySecondLexer(RegexLexer):
+ tokens = {
+ "root": [
+ (words(["[", "x"]), Token.Name),
+ ],
+ }
+
+
+def test_bracket_escape():
+ s = "whatever"
+ # This used to emit a FutureWarning.
+ assert list(MySecondLexer().get_tokens("x")) == [
+ (Token.Name, "x"),
+ (Token.Text.Whitespace, "\n"),
+ ]