summaryrefslogtreecommitdiff
path: root/support
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-05-26 03:01:06 +0000
committerNorbert Preining <norbert@preining.info>2022-05-26 03:01:06 +0000
commit02d941fa9c9895bb08a84ac9afe3559abd1ba8ad (patch)
tree481d1d368c6d295a779b590d23bcd5397bcba628 /support
parentf01a37f8311f33e32441d25bdadcda9dcdbd165d (diff)
CTAN sync 202205260301
Diffstat (limited to 'support')
-rw-r--r--support/texlab/CHANGELOG.md25
-rw-r--r--support/texlab/Cargo.lock1524
-rw-r--r--support/texlab/Cargo.toml106
-rw-r--r--support/texlab/README.md4
-rw-r--r--support/texlab/benches/bench_main.rs42
-rw-r--r--support/texlab/docs/previewing.md12
-rw-r--r--support/texlab/docs/tectonic.md50
-rw-r--r--support/texlab/src/capabilities.rs10
-rw-r--r--support/texlab/src/citation.rs196
-rw-r--r--support/texlab/src/citation/apa.csl1526
-rw-r--r--support/texlab/src/citation/bibutils.rs195
-rw-r--r--support/texlab/src/citation/driver.rs733
-rw-r--r--support/texlab/src/citation/entry.rs123
-rw-r--r--support/texlab/src/citation/field.rs4
-rw-r--r--support/texlab/src/citation/field/author.rs73
-rw-r--r--support/texlab/src/citation/field/date.rs81
-rw-r--r--support/texlab/src/citation/field/number.rs54
-rw-r--r--support/texlab/src/citation/field/text.rs263
-rw-r--r--support/texlab/src/citation/output.rs87
-rw-r--r--support/texlab/src/citation/ris.rs471
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap6
-rw-r--r--support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap6
-rw-r--r--support/texlab/src/citation/tests.rs351
-rw-r--r--support/texlab/src/component_db.rs10
-rw-r--r--support/texlab/src/config.rs88
-rw-r--r--support/texlab/src/context.rs34
-rw-r--r--support/texlab/src/diagnostics.rs14
-rw-r--r--support/texlab/src/diagnostics/bibtex.rs55
-rw-r--r--support/texlab/src/diagnostics/build_log.rs14
-rw-r--r--support/texlab/src/diagnostics/chktex.rs19
-rw-r--r--support/texlab/src/diagnostics/debouncer.rs26
-rw-r--r--support/texlab/src/diagnostics/latex.rs35
-rw-r--r--support/texlab/src/dispatch.rs10
-rw-r--r--support/texlab/src/distro/kpsewhich.rs2
-rw-r--r--support/texlab/src/distro/texlive.rs4
-rw-r--r--support/texlab/src/document.rs (renamed from support/texlab/src/workspace/document.rs)64
-rw-r--r--support/texlab/src/environment.rs32
-rw-r--r--support/texlab/src/features.rs77
-rw-r--r--support/texlab/src/features/build.rs90
-rw-r--r--support/texlab/src/features/completion.rs149
-rw-r--r--support/texlab/src/features/completion/acronym_ref.rs29
-rw-r--r--support/texlab/src/features/completion/argument.rs40
-rw-r--r--support/texlab/src/features/completion/begin_command.rs11
-rw-r--r--support/texlab/src/features/completion/citation.rs58
-rw-r--r--support/texlab/src/features/completion/color.rs21
-rw-r--r--support/texlab/src/features/completion/color_model.rs25
-rw-r--r--support/texlab/src/features/completion/component_command.rs23
-rw-r--r--support/texlab/src/features/completion/component_environment.rs41
-rw-r--r--support/texlab/src/features/completion/entry_type.rs34
-rw-r--r--support/texlab/src/features/completion/field.rs33
-rw-r--r--support/texlab/src/features/completion/glossary_ref.rs28
-rw-r--r--support/texlab/src/features/completion/import.rs28
-rw-r--r--support/texlab/src/features/completion/include.rs54
-rw-r--r--support/texlab/src/features/completion/label.rs28
-rw-r--r--support/texlab/src/features/completion/theorem.rs22
-rw-r--r--support/texlab/src/features/completion/tikz_library.rs25
-rw-r--r--support/texlab/src/features/completion/types.rs15
-rw-r--r--support/texlab/src/features/completion/user_command.rs17
-rw-r--r--support/texlab/src/features/completion/user_environment.rs16
-rw-r--r--support/texlab/src/features/completion/util.rs10
-rw-r--r--support/texlab/src/features/cursor.rs88
-rw-r--r--support/texlab/src/features/definition.rs12
-rw-r--r--support/texlab/src/features/definition/command.rs30
-rw-r--r--support/texlab/src/features/definition/document.rs20
-rw-r--r--support/texlab/src/features/definition/entry.rs39
-rw-r--r--support/texlab/src/features/definition/label.rs21
-rw-r--r--support/texlab/src/features/definition/string.rs42
-rw-r--r--support/texlab/src/features/folding.rs56
-rw-r--r--support/texlab/src/features/formatting.rs10
-rw-r--r--support/texlab/src/features/formatting/bibtex_internal.rs132
-rw-r--r--support/texlab/src/features/formatting/latexindent.rs12
-rw-r--r--support/texlab/src/features/forward_search.rs30
-rw-r--r--support/texlab/src/features/highlight.rs4
-rw-r--r--support/texlab/src/features/highlight/label.rs33
-rw-r--r--support/texlab/src/features/hover.rs31
-rw-r--r--support/texlab/src/features/hover/citation.rs66
-rw-r--r--support/texlab/src/features/hover/component.rs18
-rw-r--r--support/texlab/src/features/hover/entry_type.rs20
-rw-r--r--support/texlab/src/features/hover/field.rs29
-rw-r--r--support/texlab/src/features/hover/label.rs14
-rw-r--r--support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap25
-rw-r--r--support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap25
-rw-r--r--support/texlab/src/features/hover/string_ref.rs47
-rw-r--r--support/texlab/src/features/link.rs30
-rw-r--r--support/texlab/src/features/reference.rs12
-rw-r--r--support/texlab/src/features/reference/entry.rs72
-rw-r--r--support/texlab/src/features/reference/label.rs55
-rw-r--r--support/texlab/src/features/reference/string.rs47
-rw-r--r--support/texlab/src/features/rename.rs23
-rw-r--r--support/texlab/src/features/rename/command.rs32
-rw-r--r--support/texlab/src/features/rename/entry.rs57
-rw-r--r--support/texlab/src/features/rename/label.rs55
-rw-r--r--support/texlab/src/features/semantic.rs21
-rw-r--r--support/texlab/src/features/semantic/bibtex.rs289
-rw-r--r--support/texlab/src/features/semantic/legend.rs83
-rw-r--r--support/texlab/src/features/symbol.rs71
-rw-r--r--support/texlab/src/features/symbol/bibtex.rs40
-rw-r--r--support/texlab/src/features/symbol/latex.rs159
-rw-r--r--support/texlab/src/features/symbol/project_order.rs142
-rw-r--r--support/texlab/src/features/symbol/types.rs8
-rw-r--r--support/texlab/src/label.rs69
-rw-r--r--support/texlab/src/lib.rs12
-rw-r--r--support/texlab/src/line_index.rs2
-rw-r--r--support/texlab/src/line_index_ext.rs2
-rw-r--r--support/texlab/src/main.rs17
-rw-r--r--support/texlab/src/options.rs3
-rw-r--r--support/texlab/src/req_queue.rs5
-rw-r--r--support/texlab/src/server.rs1089
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_001.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_002.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_003.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_004.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_005.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_006.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_007.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_008.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_009.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_010.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_011.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_012.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_013.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_014.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_015.snap6
-rw-r--r--support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_016.snap6
-rw-r--r--support/texlab/src/syntax.rs14
-rw-r--r--support/texlab/src/syntax/bibtex.rs73
-rw-r--r--support/texlab/src/syntax/bibtex/ast.rs244
-rw-r--r--support/texlab/src/syntax/bibtex/cst.rs240
-rw-r--r--support/texlab/src/syntax/bibtex/kind.rs52
-rw-r--r--support/texlab/src/syntax/bibtex/lexer.rs202
-rw-r--r--support/texlab/src/syntax/bibtex/parser.rs589
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__empty.snap7
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_complete_parens.snap41
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields.snap14
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields_missing_comma.snap13
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field.snap28
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_number_key.snap28
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_trailing_comma.snap29
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields.snap43
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields_incomplete.snap34
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__junk.snap11
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_casing.snap21
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_complete.snap21
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_missing_end.snap20
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_casing.snap23
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_complete.snap23
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_concatenation.snap43
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_incomplete.snap23
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_missing_quote.snap23
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_001.snap430
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_002.snap258
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_003.snap208
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__comment.snap9
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__entry_colon.snap51
-rw-r--r--support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__preamble.snap17
-rw-r--r--support/texlab/src/syntax/bibtex/tests.rs126
-rw-r--r--support/texlab/src/syntax/build_log.rs24
-rw-r--r--support/texlab/src/syntax/latex.rs16
-rw-r--r--support/texlab/src/syntax/latex/analysis.rs19
-rw-r--r--support/texlab/src/syntax/latex/analysis/command.rs8
-rw-r--r--support/texlab/src/syntax/latex/analysis/distro_file.rs16
-rw-r--r--support/texlab/src/syntax/latex/analysis/environment.rs8
-rw-r--r--support/texlab/src/syntax/latex/analysis/explicit_link.rs26
-rw-r--r--support/texlab/src/syntax/latex/analysis/graphics_path.rs21
-rw-r--r--support/texlab/src/syntax/latex/analysis/implicit_link.rs27
-rw-r--r--support/texlab/src/syntax/latex/analysis/label_name.rs23
-rw-r--r--support/texlab/src/syntax/latex/analysis/label_number.rs6
-rw-r--r--support/texlab/src/syntax/latex/analysis/theorem.rs9
-rw-r--r--support/texlab/src/syntax/latex/analysis/types.rs22
-rw-r--r--support/texlab/src/syntax/latex/cst.rs343
-rw-r--r--support/texlab/src/syntax/latex/kind.rs10
-rw-r--r--support/texlab/src/syntax/latex/lexer.rs690
-rw-r--r--support/texlab/src/syntax/latex/parser.rs202
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__asymptote.snap43
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__block_comment.snap27
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__asymptote.snap24
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__block_comments.snap26
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap39
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition.snap4
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition_optional_arg.snap35
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_include_complicated_options.snap27
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_path.snap17
-rw-r--r--support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__latex_include_equality_sign.snap18
-rw-r--r--support/texlab/src/uri.rs85
-rw-r--r--support/texlab/src/workspace.rs232
-rw-r--r--support/texlab/src/workspace/api.rs89
-rw-r--r--support/texlab/src/workspace/children_expand.rs98
-rw-r--r--support/texlab/src/workspace/parent_expand.rs129
-rw-r--r--support/texlab/src/workspace/storage.rs145
-rw-r--r--support/texlab/src/workspace/watch.rs108
-rw-r--r--support/texlab/tests/it/common.rs (renamed from support/texlab/tests/integration/common.rs)0
-rw-r--r--support/texlab/tests/it/completion.rs (renamed from support/texlab/tests/integration/completion.rs)2
-rw-r--r--support/texlab/tests/it/document_symbol.rs (renamed from support/texlab/tests/integration/document_symbol.rs)0
-rw-r--r--support/texlab/tests/it/hover.rs (renamed from support/texlab/tests/integration/hover.rs)0
-rw-r--r--support/texlab/tests/it/issues.rs (renamed from support/texlab/tests/integration/issues.rs)0
-rw-r--r--support/texlab/tests/it/main.rs (renamed from support/texlab/tests/integration/main.rs)0
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__command_complete_entry.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__command_incomplete_entry.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__empty_document.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_complete.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap)46
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name_before.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_incomplete.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__field_complete_entry.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__field_incomplete_entry.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__bibtex__junk.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__begin_command.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap)56
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__citation.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap)28
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__citation_after.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__citation_multi_word.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__citation_multi_word.snap)16
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__citation_open_brace.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap)6
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__class_command.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap)12
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__class_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__class_import.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap)16
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap)24
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color_set.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__color_name.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap)24
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__empty_document.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__kernel_command.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap)6
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_glyph.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__label.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap)56
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__multi_line_key.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__multi_line_key.snap)3
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__package_command.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__package_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap)26
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__package_import.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__pgf_library.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__preselect_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__theorem_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap)6
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__user_command.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__completion__latex__user_environment.snap (renamed from support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap)6
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__enumerate_flat.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__enumerate_nested.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__equation_flat.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__equation_nested.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__float_flat.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__float_nested.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__section_flat.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__section_nested.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__theorem_flat.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__document_symbol__theorem_nested.snap (renamed from support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__hover__empty_bibtex_document.snap (renamed from support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__hover__empty_labtex_document.snap (renamed from support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file.snap (renamed from support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file_mumber.snap (renamed from support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__issues__510_completion_with_unmatched_braces.snap (renamed from support/texlab/tests/integration/snapshots/integration__issues__510_completion_with_unmatched_braces.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__workspace_symbol__filter_bibtex.snap (renamed from support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_figure.snap (renamed from support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_item.snap (renamed from support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_math.snap (renamed from support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap)4
-rw-r--r--support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_section.snap (renamed from support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap)4
-rw-r--r--support/texlab/tests/it/workspace_symbol.rs (renamed from support/texlab/tests/integration/workspace_symbol.rs)2
-rw-r--r--support/texlab/texlab.pdfbin82720 -> 82740 bytes
265 files changed, 8046 insertions, 8613 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md
index 4f436c33b4..7b57cddb4f 100644
--- a/support/texlab/CHANGELOG.md
+++ b/support/texlab/CHANGELOG.md
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [4.0.0] - 25.05.2022
+
+### Added
+
+- Add `--version` command line flag
+- Provide pre-built binaries for `aarch64-apple-darwin` architecture ([#591](https://github.com/latex-lsp/texlab/pull/591))
+- Autocomplete files based on `\graphicspath` ([#590](https://github.com/latex-lsp/texlab/issues/590))
+- Release `texlab` on `crates.io` ([#399](https://github.com/latex-lsp/texlab/issues/399))
+
+### Changed
+
+- _BREAKING_: Use client-side file watching instead of server-side notifications (`workspace/didChangeWatchedFiles`)
+- _BREAKING_: Bump minimum supported Rust version to 1.58.1
+- _BREAKING_: Do not use `citeproc-rs` to render citations. Instead, use a custom approach that tries to resemble the `BibLaTeX` output ([#629](https://github.com/latex-lsp/texlab/pull/629))
+
+### Fixed
+
+- Parse `\subinputfrom` command correctly ([#610](https://github.com/latex-lsp/texlab/pull/610))
+- Parse verbatim environments correctly ([#490](https://github.com/latex-lsp/texlab/issues/490))
+- Stop capturing stdout when build exits ([#588](https://github.com/latex-lsp/texlab/issues/588))
+- Fix parsing of key-value pairs ([#573](https://github.com/latex-lsp/texlab/issues/573))
+- Normalize `texlab.rootDirectory` when resolving includes ([#571](https://github.com/latex-lsp/texlab/issues/571))
+- Allow optional arguments in environment definitions ([#570](https://github.com/latex-lsp/texlab/issues/570))
+- Allow `=` in include paths ([#568](https://github.com/latex-lsp/texlab/issues/568))
+
## [3.3.2] - 26.02.2022
### Fixed
diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock
index a6ffc4530d..58549a6f3a 100644
--- a/support/texlab/Cargo.lock
+++ b/support/texlab/Cargo.lock
@@ -9,23 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
-name = "ahash"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
-
-[[package]]
-name = "ahash"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
-dependencies = [
- "getrandom 0.2.4",
- "once_cell",
- "version_check",
-]
-
-[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -35,25 +18,10 @@ dependencies = [
]
[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "any_ascii"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e"
-
-[[package]]
name = "anyhow"
-version = "1.0.54"
+version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d"
+checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "atty"
@@ -68,9 +36,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "beef"
@@ -79,78 +47,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736"
[[package]]
-name = "bibutils-sys"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "701178dfb147521ab87f4729feff1c21b69449b3f09fc263dd429aa43afb4f5c"
-dependencies = [
- "cc",
-]
-
-[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
-name = "bitvec"
-version = "0.19.6"
+name = "bstr"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
+ "lazy_static",
+ "memchr",
+ "regex-automata",
+ "serde",
]
[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
-name = "bytes"
-version = "1.1.0"
+name = "bumpalo"
+version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
-name = "cancellation"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7a879c84c21f354f13535f87ad119ac3be22ebb9097b552a0af6a78f86628c4"
-
-[[package]]
-name = "cc"
-version = "1.0.72"
+name = "byteorder"
+version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
-name = "cervine"
-version = "0.0.6"
+name = "cast"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f0db89834ef04fc63d2f136327b42d532b45def0345213d28690a3446c7bdb5"
+checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
dependencies = [
- "serde",
+ "rustc_version",
]
[[package]]
-name = "cesu8"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -162,131 +97,55 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
- "libc",
"num-integer",
"num-traits",
- "serde",
- "time",
- "winapi",
-]
-
-[[package]]
-name = "citeproc"
-version = "0.0.1"
-source = "git+https://github.com/cormacrelf/citeproc-rs?rev=2ab195a1e6f84f0ff284813ece61dc62096abbfe#2ab195a1e6f84f0ff284813ece61dc62096abbfe"
-dependencies = [
- "cfg-if 0.1.10",
- "citeproc-db",
- "citeproc-io",
- "citeproc-proc",
- "csl",
- "fnv",
- "indexmap",
- "log",
- "parking_lot 0.11.2",
- "rand",
- "salsa",
- "serde",
- "string-interner",
- "thiserror",
-]
-
-[[package]]
-name = "citeproc-db"
-version = "0.1.0"
-source = "git+https://github.com/cormacrelf/citeproc-rs?rev=2ab195a1e6f84f0ff284813ece61dc62096abbfe#2ab195a1e6f84f0ff284813ece61dc62096abbfe"
-dependencies = [
- "cfg-if 0.1.10",
- "citeproc-io",
- "csl",
- "fnv",
- "indexmap",
- "log",
- "salsa",
- "serde",
- "serde_derive",
- "string-interner",
]
[[package]]
-name = "citeproc-io"
-version = "0.1.0"
-source = "git+https://github.com/cormacrelf/citeproc-rs?rev=2ab195a1e6f84f0ff284813ece61dc62096abbfe#2ab195a1e6f84f0ff284813ece61dc62096abbfe"
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
- "cervine",
- "csl",
- "fnv",
- "html5ever",
- "itertools 0.9.0",
- "lazy-transform-str",
- "log",
- "markup5ever_rcdom",
- "nom",
- "once_cell",
- "percent-encoding",
- "phf",
- "regex",
- "serde",
- "serde_derive",
- "smartstring",
- "stringreader",
- "ucd-trie",
- "unic-segment",
- "unic-ucd-category",
- "url",
+ "bitflags",
+ "textwrap 0.11.0",
+ "unicode-width",
]
[[package]]
-name = "citeproc-proc"
-version = "0.1.0"
-source = "git+https://github.com/cormacrelf/citeproc-rs?rev=2ab195a1e6f84f0ff284813ece61dc62096abbfe#2ab195a1e6f84f0ff284813ece61dc62096abbfe"
+name = "clap"
+version = "3.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
dependencies = [
"bitflags",
- "cfg-if 0.1.10",
- "citeproc-db",
- "citeproc-io",
- "csl",
- "fnv",
- "indextree",
- "itertools 0.9.0",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
"lazy_static",
- "lexical-sort",
- "log",
- "nom",
- "petgraph 0.5.1",
- "salsa",
- "serde",
- "serde_derive",
- "smallvec",
- "string-interner",
- "strum",
- "unic-segment",
- "unic-ucd-category",
+ "textwrap 0.15.0",
]
[[package]]
-name = "clap"
-version = "2.34.0"
+name = "clap_derive"
+version = "3.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
dependencies = [
- "ansi_term",
- "atty",
- "bitflags",
- "strsim",
- "textwrap",
- "unicode-width",
- "vec_map",
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "combine"
-version = "4.6.3"
+name = "clap_lex"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062"
+checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
dependencies = [
- "bytes",
- "memchr",
+ "os_str_bytes",
]
[[package]]
@@ -309,22 +168,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
name = "crc32fast"
-version = "1.3.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
+]
+
+[[package]]
+name = "criterion"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
+dependencies = [
+ "atty",
+ "cast",
+ "clap 2.34.0",
+ "criterion-plot",
+ "csv",
+ "itertools",
+ "lazy_static",
+ "num-traits",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_cbor",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
+dependencies = [
+ "cast",
+ "itertools",
]
[[package]]
name = "crossbeam-channel"
-version = "0.5.2"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils 0.8.6",
+ "cfg-if",
+ "crossbeam-utils",
]
[[package]]
@@ -333,19 +234,20 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-epoch",
- "crossbeam-utils 0.8.6",
+ "crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.6"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
+checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils 0.8.6",
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
@@ -353,66 +255,45 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.7.2"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
- "autocfg",
- "cfg-if 0.1.10",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
-dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"lazy_static",
]
[[package]]
-name = "csl"
-version = "0.0.1"
-source = "git+https://github.com/cormacrelf/citeproc-rs?rev=2ab195a1e6f84f0ff284813ece61dc62096abbfe#2ab195a1e6f84f0ff284813ece61dc62096abbfe"
+name = "csv"
+version = "1.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
- "chrono",
- "fnv",
- "log",
- "nom",
- "roxmltree",
- "semver 0.11.0",
+ "bstr",
+ "csv-core",
+ "itoa 0.4.8",
+ "ryu",
"serde",
- "smartstring",
- "string_cache",
- "strum",
- "strum_macros",
- "thiserror",
- "url",
]
[[package]]
-name = "cstree"
-version = "0.6.1"
+name = "csv-core"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "032b71bfa953cd1eb921ac105edb854a94ac038ee1d3da8948a70f22eca40ac1"
+checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
- "fxhash",
- "lasso",
- "parking_lot 0.11.2",
- "text-size",
- "triomphe",
+ "memchr",
]
[[package]]
name = "dashmap"
-version = "5.1.0"
+version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0834a35a3fce649144119e18da2a4d8ed12ef3862f47183fd46f625d072d96c"
+checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"num_cpus",
- "parking_lot 0.12.0",
+ "parking_lot",
]
[[package]]
@@ -442,11 +323,11 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding_rs"
-version = "0.8.30"
+version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
+checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
@@ -460,41 +341,23 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "fern"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065"
+checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a"
dependencies = [
"log",
]
[[package]]
-name = "filetime"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "redox_syscall",
- "winapi",
-]
-
-[[package]]
-name = "fixedbitset"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
-
-[[package]]
name = "fixedbitset"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -502,11 +365,11 @@ checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
[[package]]
name = "flate2"
-version = "1.0.22"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
@@ -529,31 +392,6 @@ dependencies = [
]
[[package]]
-name = "fsevent-sys"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
-[[package]]
-name = "futf"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
-dependencies = [
- "mac",
- "new_debug_unreachable",
-]
-
-[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -563,68 +401,39 @@ dependencies = [
]
[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
name = "getrandom"
-version = "0.1.16"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "wasi",
]
[[package]]
-name = "getrandom"
-version = "0.2.4"
+name = "half"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "gnaw"
-version = "0.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a79786fca5da31058bdc23d4a09a3b6ef0e1a77f6982cdcf6151bcbe41290d8b"
+checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
-version = "0.9.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-dependencies = [
- "ahash 0.4.7",
-]
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hashbrown"
-version = "0.11.2"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
-dependencies = [
- "ahash 0.7.6",
-]
+checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]]
name = "heck"
-version = "0.3.3"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@@ -636,31 +445,21 @@ dependencies = [
]
[[package]]
-name = "html2md"
-version = "0.2.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61f6bf799d9770725ec13d66f4af9344e96285dc14d8e71e0fe02d272690667f"
-dependencies = [
- "html5ever",
- "jni",
- "lazy_static",
- "markup5ever_rcdom",
- "percent-encoding",
- "regex",
-]
-
-[[package]]
-name = "html5ever"
-version = "0.25.1"
+name = "human_name"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
+checksum = "29d4c0030db95c2e6e68fc240def0fc5bcbecab549c1c856a9f14ec309ccb332"
dependencies = [
- "log",
- "mac",
- "markup5ever",
- "proc-macro2",
- "quote",
- "syn",
+ "libc",
+ "phf",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smallstr",
+ "smallvec",
+ "unicode-normalization",
+ "unicode-segmentation",
+ "unidecode",
]
[[package]]
@@ -676,54 +475,25 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.8.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown 0.11.2",
]
[[package]]
-name = "indextree"
-version = "4.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "990980c3d268c9b99df35e813eca2b8d1ee08606f6d2bb325edbd0b0c68f9ffe"
-
-[[package]]
name = "indoc"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
-dependencies = [
- "unindent",
-]
-
-[[package]]
-name = "inotify"
-version = "0.9.6"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
-dependencies = [
- "bitflags",
- "inotify-sys",
- "libc",
-]
-
-[[package]]
-name = "inotify-sys"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
-dependencies = [
- "libc",
-]
+checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
[[package]]
name = "insta"
-version = "1.12.0"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c0c443f6dceb3a1cb7607c87501aa91e4b9c976044f725c2a74ca2152c91a4"
+checksum = "689960f187c43c01650c805fb6bc6f55ab944499d86d4ffe9474ad78991d8e94"
dependencies = [
"console",
"once_cell",
@@ -739,16 +509,17 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
-name = "itertools"
-version = "0.9.0"
+name = "isocountry"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
+checksum = "1ea1dc4bf0fb4904ba83ffdb98af3d9c325274e92e6e295e4151e86c96363e04"
dependencies = [
- "either",
+ "serde",
+ "thiserror",
]
[[package]]
@@ -762,68 +533,23 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
-
-[[package]]
-name = "jni"
-version = "0.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
-dependencies = [
- "cesu8",
- "combine",
- "jni-sys",
- "log",
- "thiserror",
- "walkdir",
-]
-
-[[package]]
-name = "jni-sys"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
-
-[[package]]
-name = "kqueue"
-version = "1.0.4"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "058a107a784f8be94c7d35c1300f4facced2e93d2fbe5b1452b44e905ddca4a9"
-dependencies = [
- "kqueue-sys",
- "libc",
-]
-
-[[package]]
-name = "kqueue-sys"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
-dependencies = [
- "bitflags",
- "libc",
-]
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
-name = "lasso"
-version = "0.5.1"
+name = "itoa"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8647c8a01e5f7878eacb2c323c4c949fdb63773110f0686c7810769874b7e0a"
-dependencies = [
- "hashbrown 0.11.2",
-]
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
-name = "lazy-transform-str"
-version = "0.0.6"
+name = "js-sys"
+version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3f2f08de5da0353709e66c6302747c07d60186e70d19b172dbdcc51a1de357e"
+checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
- "cervine",
- "gnaw",
- "smartstring",
+ "wasm-bindgen",
]
[[package]]
@@ -833,19 +559,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
-name = "lexical-sort"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c09e4591611e231daf4d4c685a66cb0410cc1e502027a20ae55f2bb9e997207a"
-dependencies = [
- "any_ascii",
-]
-
-[[package]]
name = "libc"
-version = "0.2.113"
+version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "linked-hash-map"
@@ -855,20 +572,21 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
+ "autocfg",
"scopeguard",
]
[[package]]
name = "log"
-version = "0.4.14"
+version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
@@ -897,9 +615,9 @@ dependencies = [
[[package]]
name = "lsp-server"
-version = "0.5.2"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c351c75989da23b355226dc188dc2b52538a7f4f218d70fd7393c6b62b110444"
+checksum = "f70570c1c29cf6654029b8fe201a5507c153f0d85be6f234d471d756bc36775a"
dependencies = [
"crossbeam-channel",
"log",
@@ -909,9 +627,9 @@ dependencies = [
[[package]]
name = "lsp-types"
-version = "0.92.0"
+version = "0.93.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a69d4142d51b208c9fc3cea68b1a7fcef30354e7aa6ccad07250fd8430fc76"
+checksum = "70c74e2173b2b31f8655d33724b4b45ac13f439386f66290f539c22b144c2212"
dependencies = [
"bitflags",
"serde",
@@ -921,38 +639,6 @@ dependencies = [
]
[[package]]
-name = "mac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
-
-[[package]]
-name = "markup5ever"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
-dependencies = [
- "log",
- "phf",
- "phf_codegen",
- "string_cache",
- "string_cache_codegen",
- "tendril",
-]
-
-[[package]]
-name = "markup5ever_rcdom"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
-dependencies = [
- "html5ever",
- "markup5ever",
- "tendril",
- "xml5ever",
-]
-
-[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -960,9 +646,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
@@ -975,34 +661,11 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.4.4"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
"adler",
- "autocfg",
-]
-
-[[package]]
-name = "mio"
-version = "0.7.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
-dependencies = [
- "libc",
- "log",
- "miow",
- "ntapi",
- "winapi",
-]
-
-[[package]]
-name = "miow"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
-dependencies = [
- "winapi",
]
[[package]]
@@ -1015,55 +678,10 @@ dependencies = [
]
[[package]]
-name = "new_debug_unreachable"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
-
-[[package]]
-name = "nom"
-version = "6.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
-dependencies = [
- "bitvec",
- "funty",
- "memchr",
- "version_check",
-]
-
-[[package]]
-name = "notify"
-version = "5.0.0-pre.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245d358380e2352c2d020e8ee62baac09b3420f1f6c012a31326cfced4ad487d"
-dependencies = [
- "bitflags",
- "crossbeam-channel",
- "filetime",
- "fsevent-sys",
- "inotify",
- "kqueue",
- "libc",
- "mio",
- "walkdir",
- "winapi",
-]
-
-[[package]]
-name = "ntapi"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
-dependencies = [
- "winapi",
-]
-
-[[package]]
name = "num-integer"
-version = "0.1.44"
+version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
@@ -1071,9 +689,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
@@ -1090,9 +708,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.9.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "oorandom"
@@ -1101,15 +719,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
-name = "parking_lot"
-version = "0.11.2"
+name = "os_str_bytes"
+version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core 0.8.5",
-]
+checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
[[package]]
name = "parking_lot"
@@ -1118,30 +731,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
- "parking_lot_core 0.9.1",
+ "parking_lot_core",
]
[[package]]
name = "parking_lot_core"
-version = "0.8.5"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
- "cfg-if 1.0.0",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
-dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"redox_syscall",
"smallvec",
@@ -1155,39 +754,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
-name = "pest"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
-dependencies = [
- "ucd-trie",
-]
-
-[[package]]
-name = "petgraph"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
-dependencies = [
- "fixedbitset 0.2.0",
- "indexmap",
-]
-
-[[package]]
name = "petgraph"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
+checksum = "51b305cc4569dd4e8765bab46261f67ef5d4d11a4b6e745100ee5dad8948b46c"
dependencies = [
- "fixedbitset 0.4.1",
+ "fixedbitset",
"indexmap",
]
[[package]]
name = "phf"
-version = "0.8.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_macros",
"phf_shared",
@@ -1195,20 +775,10 @@ dependencies = [
]
[[package]]
-name = "phf_codegen"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
-dependencies = [
- "phf_generator",
- "phf_shared",
-]
-
-[[package]]
name = "phf_generator"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
"rand",
@@ -1216,9 +786,9 @@ dependencies = [
[[package]]
name = "phf_macros"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
dependencies = [
"phf_generator",
"phf_shared",
@@ -1230,24 +800,46 @@ dependencies = [
[[package]]
name = "phf_shared"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
-name = "ppv-lite86"
-version = "0.2.16"
+name = "plotters"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9"
+dependencies = [
+ "plotters-backend",
+]
[[package]]
-name = "precomputed-hash"
-version = "0.1.1"
+name = "ppv-lite86"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
@@ -1281,47 +873,38 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.14"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
-name = "radium"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
-
-[[package]]
name = "rand"
-version = "0.7.3"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
- "getrandom 0.1.16",
"libc",
"rand_chacha",
"rand_core",
- "rand_hc",
- "rand_pcg",
]
[[package]]
name = "rand_chacha"
-version = "0.2.2"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
@@ -1329,36 +912,18 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.5.1"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
- "getrandom 0.1.16",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
-dependencies = [
- "rand_core",
+ "getrandom",
]
[[package]]
name = "rayon"
-version = "1.5.1"
+version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
@@ -1368,31 +933,30 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.9.1"
+version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
- "crossbeam-utils 0.8.6",
- "lazy_static",
+ "crossbeam-utils",
"num_cpus",
]
[[package]]
name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
-version = "1.5.4"
+version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
"aho-corasick",
"memchr",
@@ -1400,10 +964,16 @@ dependencies = [
]
[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+
+[[package]]
name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "remove_dir_all"
@@ -1415,12 +985,16 @@ dependencies = [
]
[[package]]
-name = "roxmltree"
-version = "0.13.1"
+name = "rowan"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbf7d7b1ea646d380d0e8153158063a6da7efe30ddbf3184042848e3f8a6f671"
+checksum = "a4c1112d7b23c800be3a0dae244886b71d96b1461b57b31b56e4c679acbe614f"
dependencies = [
- "xmlparser",
+ "countme",
+ "hashbrown 0.12.1",
+ "memoffset",
+ "rustc-hash",
+ "text-size",
]
[[package]]
@@ -1435,43 +1009,20 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
- "semver 1.0.4",
+ "semver",
]
[[package]]
-name = "ryu"
-version = "1.0.9"
+name = "rustversion"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
-name = "salsa"
-version = "0.15.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ab29056d4fb4048a5f0d169c9b6e5526160c9ec37aded5a6879c2c9c445a8e4"
-dependencies = [
- "crossbeam-utils 0.7.2",
- "indexmap",
- "lock_api",
- "log",
- "oorandom",
- "parking_lot 0.11.2",
- "rustc-hash",
- "salsa-macros",
- "smallvec",
-]
-
-[[package]]
-name = "salsa-macros"
-version = "0.15.2"
+name = "ryu"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1c3aec007c63c4ed4cd7a018529fb0b5575c4562575fc6a40d6cd2ae0b792ef"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "same-file"
@@ -1490,43 +1041,34 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser",
- "serde",
-]
-
-[[package]]
-name = "semver"
-version = "1.0.4"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
+checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
[[package]]
-name = "semver-parser"
-version = "0.10.2"
+name = "serde"
+version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
- "pest",
+ "serde_derive",
]
[[package]]
-name = "serde"
-version = "1.0.136"
+name = "serde_cbor"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
- "serde_derive",
+ "half",
+ "serde",
]
[[package]]
name = "serde_derive"
-version = "1.0.136"
+version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
@@ -1535,20 +1077,20 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.79"
+version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
- "itoa",
+ "itoa 1.0.2",
"ryu",
"serde",
]
[[package]]
name = "serde_repr"
-version = "0.1.7"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
+checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed"
dependencies = [
"proc-macro2",
"quote",
@@ -1557,9 +1099,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
-version = "0.8.23"
+version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
+checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc"
dependencies = [
"indexmap",
"ryu",
@@ -1575,162 +1117,74 @@ checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
[[package]]
name = "siphasher"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e"
-
-[[package]]
-name = "smallvec"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
-
-[[package]]
-name = "smartstring"
-version = "0.2.9"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31aa6a31c0c2b21327ce875f7e8952322acfcfd0c27569a6e18a647281352c9b"
-dependencies = [
- "serde",
- "static_assertions",
-]
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
-name = "smol_str"
-version = "0.1.21"
+name = "smallstr"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61d15c83e300cce35b7c8cd39ff567c1ef42dde6d4a1a38dbdbf9a59902261bd"
+checksum = "1e922794d168678729ffc7e07182721a14219c65814e66e91b839a272fe5ae4f"
dependencies = [
- "serde",
+ "smallvec",
]
[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "string-interner"
-version = "0.12.2"
+name = "smallvec"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "383196d1876517ee6f9f0864d1fc1070331b803335d3c6daaa04bbcccd823c08"
-dependencies = [
- "cfg-if 1.0.0",
- "hashbrown 0.9.1",
- "serde",
-]
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
-name = "string_cache"
-version = "0.8.2"
+name = "smol_str"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
+checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44"
dependencies = [
- "lazy_static",
- "new_debug_unreachable",
- "parking_lot 0.11.2",
- "phf_shared",
- "precomputed-hash",
"serde",
]
[[package]]
-name = "string_cache_codegen"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
-dependencies = [
- "phf_generator",
- "phf_shared",
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "stringreader"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "913e7b03d63752f6cdd2df77da36749d82669904798fe8944b9ec3d23f159905"
-
-[[package]]
-name = "strsim"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
-[[package]]
-name = "structopt"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
-dependencies = [
- "clap",
- "lazy_static",
- "structopt-derive",
-]
-
-[[package]]
-name = "structopt-derive"
-version = "0.4.18"
+name = "strum"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
+ "strum_macros",
]
[[package]]
-name = "strum"
-version = "0.19.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
-
-[[package]]
name = "strum_macros"
-version = "0.19.4"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
+checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck",
"proc-macro2",
"quote",
+ "rustversion",
"syn",
]
[[package]]
name = "syn"
-version = "1.0.86"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
"proc-macro2",
"quote",
- "unicode-xid",
+ "unicode-ident",
]
[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"fastrand",
"libc",
"redox_syscall",
@@ -1739,17 +1193,6 @@ dependencies = [
]
[[package]]
-name = "tendril"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9ef557cb397a4f0a5a3a628f06515f78563f2209e64d47055d9dc6052bf5e33"
-dependencies = [
- "futf",
- "mac",
- "utf-8",
-]
-
-[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1761,51 +1204,46 @@ dependencies = [
[[package]]
name = "texlab"
-version = "3.3.2"
+version = "4.0.0"
dependencies = [
"anyhow",
- "bibutils-sys",
"byteorder",
- "cancellation",
- "cfg-if 1.0.0",
- "citeproc",
- "citeproc-db",
- "citeproc-io",
+ "chrono",
+ "clap 3.1.18",
+ "criterion",
"crossbeam-channel",
- "csl",
- "cstree",
"dashmap",
"derive_more",
"encoding_rs",
"encoding_rs_io",
"fern",
"flate2",
- "fnv",
"fuzzy-matcher",
- "html2md",
+ "human_name",
"indoc",
"insta",
- "itertools 0.10.3",
+ "isocountry",
+ "itertools",
"log",
"logos",
"lsp-server",
"lsp-types",
"multimap",
- "notify",
"once_cell",
- "petgraph 0.6.0",
- "rayon",
+ "petgraph",
"regex",
+ "rowan",
"rustc-hash",
"serde",
"serde_json",
"serde_repr",
"smol_str",
- "structopt",
+ "strum",
"tempfile",
"threadpool",
"titlecase",
"typed-builder",
+ "unicode-normalization",
"unindent",
"url",
"uuid",
@@ -1827,19 +1265,25 @@ dependencies = [
]
[[package]]
+name = "textwrap"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+
+[[package]]
name = "thiserror"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
@@ -1848,9 +1292,9 @@ dependencies = [
[[package]]
name = "thread_local"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
@@ -1865,21 +1309,20 @@ dependencies = [
]
[[package]]
-name = "time"
-version = "0.1.44"
+name = "tinytemplate"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi",
+ "serde",
+ "serde_json",
]
[[package]]
name = "tinyvec"
-version = "1.5.1"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
@@ -1901,17 +1344,6 @@ dependencies = [
]
[[package]]
-name = "triomphe"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c45e322b26410d7260e00f64234810c2f17d7ece356182af4df8f7ff07890f09"
-dependencies = [
- "memoffset",
- "serde",
- "stable_deref_trait",
-]
-
-[[package]]
name = "typed-builder"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1923,78 +1355,16 @@ dependencies = [
]
[[package]]
-name = "ucd-trie"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
-
-[[package]]
-name = "unic-char-property"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
-dependencies = [
- "unic-char-range",
-]
-
-[[package]]
-name = "unic-char-range"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
-
-[[package]]
-name = "unic-common"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
-
-[[package]]
-name = "unic-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
-dependencies = [
- "unic-ucd-segment",
-]
-
-[[package]]
-name = "unic-ucd-category"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0"
-dependencies = [
- "matches",
- "unic-char-property",
- "unic-char-range",
- "unic-ucd-version",
-]
-
-[[package]]
-name = "unic-ucd-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
-dependencies = [
- "unic-char-property",
- "unic-char-range",
- "unic-ucd-version",
-]
-
-[[package]]
-name = "unic-ucd-version"
-version = "0.9.0"
+name = "unicode-bidi"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
-dependencies = [
- "unic-common",
-]
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
-name = "unicode-bidi"
-version = "0.3.7"
+name = "unicode-ident"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "unicode-normalization"
@@ -2007,9 +1377,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
-version = "1.8.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
@@ -2018,16 +1388,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "unidecode"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc"
[[package]]
name = "unindent"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
+checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44"
[[package]]
name = "url"
@@ -2043,33 +1413,21 @@ dependencies = [
]
[[package]]
-name = "utf-8"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
-[[package]]
name = "utf8-ranges"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
+checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba"
[[package]]
name = "uuid"
-version = "0.8.2"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+checksum = "93bbc61e655a4833cf400d0d15bf3649313422fa7572886ad6dab16d79886365"
dependencies = [
- "getrandom 0.2.4",
+ "getrandom",
]
[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2088,15 +1446,73 @@ dependencies = [
[[package]]
name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
+name = "wasm-bindgen"
+version = "0.2.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
+
+[[package]]
+name = "web-sys"
+version = "0.3.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
[[package]]
name = "winapi"
@@ -2131,9 +1547,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
-version = "0.32.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
@@ -2144,57 +1560,33 @@ dependencies = [
[[package]]
name = "windows_aarch64_msvc"
-version = "0.32.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
-version = "0.32.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
-version = "0.32.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.32.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
-
-[[package]]
-name = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
-
-[[package]]
-name = "xml5ever"
-version = "0.16.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9234163818fd8e2418fcde330655e757900d4236acd8cc70fef345ef91f6d865"
-dependencies = [
- "log",
- "mac",
- "markup5ever",
- "time",
-]
-
-[[package]]
-name = "xmlparser"
-version = "0.13.3"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "yaml-rust"
diff --git a/support/texlab/Cargo.toml b/support/texlab/Cargo.toml
index ad6a333479..df04cfba31 100644
--- a/support/texlab/Cargo.toml
+++ b/support/texlab/Cargo.toml
@@ -1,85 +1,93 @@
[package]
name = "texlab"
description = "LaTeX Language Server"
-version = "3.3.2"
+version = "4.0.0"
license = "GPL-3.0"
readme = "README.md"
authors = [
"Eric Förster <eric.foerster@outlook.com>",
"Patrick Förster <patrick.foerster@outlook.de>",
]
-edition = "2018"
+edition = "2021"
+rust-version = "1.58.1"
homepage = "https://github.com/latex-lsp/texlab"
-resolver = "2"
+repository = "https://github.com/latex-lsp/texlab"
+documentation = "https://github.com/latex-lsp/texlab"
+keywords = ["lsp", "server", "latex", "bibtex"]
+categories = ["development-tools"]
+exclude = [
+ ".gitattributes",
+ ".gitignore",
+ ".github/**",
+ "tests/it/**",
+ "*.snap",
+ "texlab.1",
+ "texlab.pdf",
+ "texlab.tex",
+]
[lib]
doctest = false
[features]
-default = ["citation", "completion"]
+default = ["completion"]
completion = ["fuzzy-matcher"]
-citation = [
- "bibutils-sys",
- "citeproc",
- "citeproc-db",
- "citeproc-io",
- "csl",
- "fnv",
- "html2md",
-]
-semantic = []
[dependencies]
-anyhow = "1.0.54"
-bibutils-sys = { version = "0.1.1", optional = true }
+anyhow = "1.0.57"
byteorder = "1.4.3"
-cancellation = "0.1"
-cfg-if = "1.0.0"
-citeproc = { git = "https://github.com/cormacrelf/citeproc-rs", rev = "2ab195a1e6f84f0ff284813ece61dc62096abbfe", optional = true }
-citeproc-db = { git = "https://github.com/cormacrelf/citeproc-rs", rev = "2ab195a1e6f84f0ff284813ece61dc62096abbfe", optional = true }
-citeproc-io = { git = "https://github.com/cormacrelf/citeproc-rs", rev = "2ab195a1e6f84f0ff284813ece61dc62096abbfe", optional = true }
-csl = { git = "https://github.com/cormacrelf/citeproc-rs", rev = "2ab195a1e6f84f0ff284813ece61dc62096abbfe", optional = true }
-crossbeam-channel = "0.5.2"
-cstree = "0.6.1"
-dashmap = "5.1.0"
+chrono = { version = "0.4.19", default-features = false, features = ["std"] }
+clap = { version = "3.1.17", features = [
+ "std",
+ "derive",
+], default-features = false }
+crossbeam-channel = "0.5.4"
+dashmap = "~5.2.0"
derive_more = "0.99.17"
-encoding_rs = "0.8.30"
+encoding_rs = "0.8.31"
encoding_rs_io = "0.1.7"
-flate2 = "1.0.22"
fern = "0.6"
-fnv = { version = "1.0", optional = true }
+flate2 = "1.0.23"
fuzzy-matcher = { version = "0.3.7", optional = true }
-html2md = { version = "0.2.13", optional = true }
+human_name = { version = "1.0.3", default-features = false }
+isocountry = "0.3.2"
itertools = "0.10.1"
-log = "0.4.14"
+log = "0.4.17"
logos = "0.12.0"
-lsp-server = "0.5"
-lsp-types = "0.92.0"
+lsp-server = "0.6.0"
+lsp-types = "0.93.0"
multimap = "0.8.3"
-notify = "5.0.0-pre.13"
-once_cell = "1.9.0"
+once_cell = "1.10.0"
petgraph = "0.6.0"
-rayon = "1.5.1"
-regex = "1.5.4"
+regex = "1.5.5"
+rowan = "0.15.4"
rustc-hash = "1.1.0"
-serde = "1.0.136"
-serde_json = "1.0.79"
-serde_repr = "0.1.7"
-smol_str = { version = "0.1.21", features = ["serde"] }
-structopt = "0.3"
+serde = "1.0.137"
+serde_json = "1.0.81"
+serde_repr = "0.1.8"
+smol_str = { version = "0.1.23", features = ["serde"] }
+strum = { version = "0.24.0", features = ["derive"] }
tempfile = "3.3.0"
threadpool = "1.8.1"
titlecase = "1.1.0"
-url = "2.2.2"
-uuid = { version = "0.8.2", features = ["v4"] }
+unicode-normalization = "0.1.19"
+url = { version = "2.2.2", features = ["serde"] }
+uuid = { version = "1.0.0", features = ["v4"] }
[dev-dependencies]
-indoc = "1.0.4"
-insta = { version = "1.12.0", features = ["backtrace"] }
+criterion = { version = "0.3.5" }
+indoc = "1.0.6"
+insta = { version = "1.14.0", features = ["backtrace"] }
typed-builder = "0.10.0"
-unindent = "0.1.8"
-
-[build-dependencies]
+unindent = "0.1.9"
[profile.release]
-lto = true
+lto = "fat"
+
+[profile.bench]
+lto = "thin"
+
+[[bench]]
+name = "bench_main"
+path = "benches/bench_main.rs"
+harness = false
diff --git a/support/texlab/README.md b/support/texlab/README.md
index dc637ae4a9..1b4bd527ec 100644
--- a/support/texlab/README.md
+++ b/support/texlab/README.md
@@ -1,6 +1,6 @@
[![CI](https://github.com/latex-lsp/texlab/workflows/CI/badge.svg)](https://github.com/latex-lsp/texlab/actions)
[![Coverage](https://codecov.io/gh/latex-lsp/texlab/branch/master/graph/badge.svg)](https://codecov.io/gh/latex-lsp/texlab)
-[![Rust](https://img.shields.io/badge/rustc-1.53%2B-blue)](https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html)
+[![Rust](https://img.shields.io/badge/rustc-1.58.1%2B-blue)](https://blog.rust-lang.org/2022/01/20/Rust-1.58.1.html)
[![GitHub release](https://img.shields.io/github/release/latex-lsp/texlab?label=github)](https://github.com/latex-lsp/texlab/releases)
[![CTAN](https://img.shields.io/ctan/v/texlab)](https://ctan.org/pkg/texlab)
@@ -42,7 +42,7 @@ On Windows, you may need to install [Microsoft Visual C++ Redistributable for Vi
You will need to install the following dependencies to compile the server:
-- A recent, stable version of [Rust](https://rustup.rs/) (>= 1.53)
+- A recent, stable version of [Rust](https://rustup.rs/)
Then run the following command in the project folder:
diff --git a/support/texlab/benches/bench_main.rs b/support/texlab/benches/bench_main.rs
new file mode 100644
index 0000000000..44364c5d5e
--- /dev/null
+++ b/support/texlab/benches/bench_main.rs
@@ -0,0 +1,42 @@
+use std::sync::Arc;
+
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use lsp_types::{
+ CompletionParams, Position, TextDocumentIdentifier, TextDocumentPositionParams, Url,
+};
+use texlab::{features::FeatureRequest, syntax::latex, DocumentLanguage, Workspace};
+
+fn criterion_benchmark(c: &mut Criterion) {
+ c.bench_function("LaTeX/Parser", |b| {
+ b.iter(|| latex::parse(black_box(include_str!("../texlab.tex"))));
+ });
+
+ c.bench_function("LaTeX/Completion/Command", |b| {
+ let uri = Arc::new(Url::parse("http://example.com/texlab.tex").unwrap());
+ let text = Arc::new(include_str!("../texlab.tex").to_string());
+ let mut workspace = Workspace::default();
+ workspace
+ .open(Arc::clone(&uri), text, DocumentLanguage::Latex)
+ .unwrap();
+
+ b.iter(|| {
+ texlab::features::complete(FeatureRequest {
+ params: CompletionParams {
+ context: None,
+ partial_result_params: Default::default(),
+ work_done_progress_params: Default::default(),
+ text_document_position: TextDocumentPositionParams::new(
+ TextDocumentIdentifier::new(uri.as_ref().clone()),
+ Position::new(0, 1),
+ ),
+ },
+ workspace: workspace.clone(),
+ uri: Arc::clone(&uri),
+ })
+ });
+ });
+}
+
+criterion_group!(benches, criterion_benchmark);
+
+criterion_main!(benches);
diff --git a/support/texlab/docs/previewing.md b/support/texlab/docs/previewing.md
index fbf6679760..6c667e0fd0 100644
--- a/support/texlab/docs/previewing.md
+++ b/support/texlab/docs/previewing.md
@@ -16,11 +16,17 @@ Previewing can be configured in a variety of ways:
because `texlab` does not get notified by `latexmk` when a document gets built.
Instead, you can use `texlab.build.onSave`.
-In the following sections, we will give forward search configurations for several popular viewers
-and Visual Studio Code.
-However, these settings can easily be adapted to other editors.
+In the following sections, we will give forward search configurations for several popular viewers.
If your viewer is not listed here, you can send us a pull request or create an issue.
+The inverse search configuration depends on the editor.
+In this section, we will give instructions for [Visual Studio Code](https://code.visualstudio.com/).
+However, these settings can easily be adapted to other editors.
+Some plugins can help with setting up inverse search:
+
+- `Neovim`: [`f3fora/nvim-texlabconfig`](https://github.com/f3fora/nvim-texlabconfig)
+- `Emacs`: [`ROCKTAKEY/lsp-latex`](https://github.com/ROCKTAKEY/lsp-latex#inverse-search)
+
---
## SumatraPDF
diff --git a/support/texlab/docs/tectonic.md b/support/texlab/docs/tectonic.md
index 38e0a7d636..e75fd4a3ee 100644
--- a/support/texlab/docs/tectonic.md
+++ b/support/texlab/docs/tectonic.md
@@ -1,26 +1,48 @@
-### Example: Using `tectonic`
+# Usage with `tectonic`
-[`tectonic`](https://tectonic-typesetting.github.io/) is an alternative to `latexmk`, built in Rust.
-You can quickly get started by changing `.vscode/settings.json` in your workspace to include the following:
+[`tectonic`](https://tectonic-typesetting.github.io/) is a modernized, alternative TeX engine.
+Most features of `texlab` work out of the box when using `tectonic`.
+To compile documents through `texlab`, you need to change the configuration.
+See `tectonic --help` for more information about the flags.
-```jsonc
+---
+
+**Hint:**
+
+Please make sure to set `texlab.auxDirectory` if you change the build directory with the `--outdir` argument.
+
+Also, `--keep-intermediates` is recommended because they allow `texlab`
+to find out the section numbers and show them in the completion.
+Without the `--keep-logs` flag, `texlab` won't be able to report compilation warnings.
+
+---
+
+## V2 CLI
+
+```json
+{
+ "texlab.build.executable": "tectonic",
+ "texlab.build.args": [
+ "-X",
+ "compile"
+ "%f",
+ "--synctex",
+ "--keep-logs",
+ "--keep-intermediates"
+ ]
+}
+```
+
+## V1 CLI
+
+```json
{
- // See `tectonic --help` for the format
"texlab.build.executable": "tectonic",
"texlab.build.args": [
- // Input
"%f",
- // Flags
"--synctex",
"--keep-logs",
"--keep-intermediates"
- // Options
- // OPTIONAL: If you want a custom out directory,
- // uncomment the following line.
- //"--outdir out",
]
- // OPTIONAL: The server needs to be configured
- // to read the logs from the out directory as well.
- // "texlab.auxDirectory": "out",
}
```
diff --git a/support/texlab/src/capabilities.rs b/support/texlab/src/capabilities.rs
index e20a66b1ba..bcb5bbba8b 100644
--- a/support/texlab/src/capabilities.rs
+++ b/support/texlab/src/capabilities.rs
@@ -12,6 +12,8 @@ pub trait ClientCapabilitiesExt {
fn has_pull_configuration_support(&self) -> bool;
fn has_push_configuration_support(&self) -> bool;
+
+ fn has_file_watching_support(&self) -> bool;
}
impl ClientCapabilitiesExt for ClientCapabilities {
@@ -55,6 +57,14 @@ impl ClientCapabilitiesExt for ClientCapabilities {
.and_then(|cap| cap.dynamic_registration)
== Some(true)
}
+
+ fn has_file_watching_support(&self) -> bool {
+ self.workspace
+ .as_ref()
+ .and_then(|cap| cap.did_change_watched_files)
+ .and_then(|cap| cap.dynamic_registration)
+ == Some(true)
+ }
}
#[cfg(test)]
diff --git a/support/texlab/src/citation.rs b/support/texlab/src/citation.rs
index 5be86561c9..e1d6f49268 100644
--- a/support/texlab/src/citation.rs
+++ b/support/texlab/src/citation.rs
@@ -1,168 +1,36 @@
-mod bibutils;
-mod ris;
-
-use std::sync::Arc;
-
-use citeproc::{prelude::SupportedFormat, ClusterPosition, InitOptions, Processor};
-use citeproc_db::PredefinedLocales;
-use citeproc_io::{Cite, Reference};
-use lsp_types::{MarkupContent, MarkupKind};
-use once_cell::sync::Lazy;
-use regex::Regex;
-
-use crate::syntax::{bibtex, CstNode};
-
-use self::{
- bibutils::*,
- ris::{RisLibrary, RisReference},
-};
-
-static APA_STYLE: &str = include_str!("citation/apa.csl");
-
-static DOI_URL_PATTERN: &str = r#"https://doi.org/\[.*\]\(.*\)"#;
-
-static DOI_URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(DOI_URL_PATTERN).unwrap());
-
-pub fn render_citation(root: &bibtex::SyntaxNode, key: &str) -> Option<MarkupContent> {
- let ris_reference = convert_to_ris(root, key)?;
- let doi_url = get_doi_url_markdown(&ris_reference);
- let csl_reference: Reference = ris_reference.into();
- let html = generate_bibliography(csl_reference)?;
-
- let mut markdown = html2md::parse_html(&html).trim().to_owned();
- if markdown == "" {
- return None;
- }
-
- if let Some(doi_url) = doi_url {
- markdown = DOI_URL_REGEX
- .replace(&markdown, doi_url.as_str())
- .into_owned();
- }
-
- markdown = markdown
- .replace("..", ".")
- .replace("\\\'", "'")
- .replace("\\-", "-")
- .replace("\\\\textsubscript", "")
- .replace("\\\\textsuperscript", "");
- let content = MarkupContent {
- kind: MarkupKind::Markdown,
- value: markdown,
- };
- Some(content)
-}
-
-fn convert_to_ris(root: &bibtex::SyntaxNode, key: &str) -> Option<RisReference> {
- let mut bib_code = String::new();
- for string in root
- .children()
- .filter_map(bibtex::String::cast)
- .filter(|string| string.name().is_some())
- {
- bib_code.push_str(&string.syntax().to_string());
+mod driver;
+mod entry;
+mod field;
+mod output;
+
+use unicode_normalization::UnicodeNormalization;
+
+use crate::syntax::bibtex;
+
+use self::{driver::Driver, output::Inline};
+
+pub fn render(entry: &bibtex::Entry) -> Option<String> {
+ let mut output = String::new();
+ let mut driver = Driver::default();
+ driver.process(entry);
+ driver.finish().for_each(|(inline, punct)| {
+ let text = match inline {
+ Inline::Regular(text) => text,
+ Inline::Italic(text) => format!("*{text}*"),
+ Inline::Quoted(text) => format!("\"{text}\""),
+ Inline::Link { url, alt } => format!("[{alt}]({url})"),
+ };
+ output.push_str(&text);
+ output.push_str(punct.as_str());
+ });
+
+ if output.is_empty() {
+ None
+ } else {
+ output.push('.');
+ Some(output.nfc().collect())
}
-
- let entry = root
- .children()
- .filter_map(bibtex::Entry::cast)
- .find(|entry| entry.key().map(|key| key.to_string()).as_deref() == Some(key))
- .filter(|entry| entry.fields().next().is_some())?;
-
- bib_code.push_str(&entry.syntax().to_string());
-
- bib_code = bib_code.replace("\\hypen", "-");
-
- let ris_code = bibutils::convert(&bib_code, InputFormat::Biblatex, OutputFormat::Ris)?;
- let ris_lib = RisLibrary::parse(ris_code.lines());
- ris_lib
- .references
- .into_iter()
- .find(|reference| reference.id.as_ref().map(AsRef::as_ref) == Some(key))
-}
-
-fn get_doi_url_markdown(ris_reference: &RisReference) -> Option<String> {
- ris_reference
- .doi
- .as_ref()
- .map(|doi| format!("[doi:{}](https://doi.org/{})", doi, doi))
-}
-
-fn generate_bibliography(reference: Reference) -> Option<String> {
- let mut processor = Processor::new(InitOptions {
- style: APA_STYLE,
- format: SupportedFormat::Html,
- fetcher: Some(Arc::new(PredefinedLocales::bundled_en_us())),
- ..InitOptions::default()
- })
- .ok()?;
- let cite = Cite::basic(&reference.id);
- let cluster_id = processor.cluster_id("texlab");
- processor.insert_reference(reference);
- processor.insert_cites(cluster_id, &[cite]);
- processor
- .set_cluster_order(&[ClusterPosition {
- id: Some(cluster_id),
- note: Some(1),
- }])
- .unwrap();
- Some(processor.get_bibliography().pop()?.value.to_string())
}
#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_simple() {
- let document = bibtex::parse(
- r#"
- @article{foo,
- author = {Foo Bar},
- title = {Baz Qux},
- year = {2020}
- }
- "#,
- );
-
- let actual_md = render_citation(&document.root, "foo").unwrap();
-
- let expected_md = MarkupContent {
- kind: MarkupKind::Markdown,
- value: "Bar, Foo. (2020). *Baz Qux*.".into(),
- };
-
- assert_eq!(actual_md, expected_md);
- }
-
- #[test]
- fn test_string() {
- let document = bibtex::parse(
- r#"
- @string{author = "Foo Bar"}
- @article{foo,
- author = author,
- title = {Baz Qux},
- year = {2020}
- }
- "#,
- );
- let actual_md = render_citation(&document.root, "foo").unwrap();
-
- let expected_md = MarkupContent {
- kind: MarkupKind::Markdown,
- value: "Bar, Foo. (2020). *Baz Qux*.".into(),
- };
-
- assert_eq!(actual_md, expected_md);
- }
-
- #[test]
- fn test_unknown_key() {
- let document = bibtex::parse("");
-
- let actual_md = render_citation(&document.root, "foo");
-
- assert_eq!(actual_md, None);
- }
-}
+mod tests;
diff --git a/support/texlab/src/citation/apa.csl b/support/texlab/src/citation/apa.csl
deleted file mode 100644
index 8454246a98..0000000000
--- a/support/texlab/src/citation/apa.csl
+++ /dev/null
@@ -1,1526 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="never" page-range-format="expanded">
- <info>
- <title>American Psychological Association 6th edition</title>
- <title-short>APA</title-short>
- <id>http://www.zotero.org/styles/apa</id>
- <link href="http://www.zotero.org/styles/apa" rel="self"/>
- <link href="http://owl.english.purdue.edu/owl/resource/560/01/" rel="documentation"/>
- <author>
- <name>Simon Kornblith</name>
- <email>simon@simonster.com</email>
- </author>
- <author>
- <name> Brenton M. Wiernik</name>
- <email>zotero@wiernik.org</email>
- </author>
- <contributor>
- <name>Bruce D'Arcus</name>
- </contributor>
- <contributor>
- <name>Curtis M. Humphrey</name>
- </contributor>
- <contributor>
- <name>Richard Karnesky</name>
- <email>karnesky+zotero@gmail.com</email>
- <uri>http://arc.nucapt.northwestern.edu/Richard_Karnesky</uri>
- </contributor>
- <contributor>
- <name>Sebastian Karcher</name>
- </contributor>
- <category citation-format="author-date"/>
- <category field="psychology"/>
- <category field="generic-base"/>
- <updated>2016-09-28T13:09:49+00:00</updated>
- <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
- </info>
- <locale xml:lang="en">
- <terms>
- <term name="editortranslator" form="short">
- <single>ed. &amp; trans.</single>
- <multiple>eds. &amp; trans.</multiple>
- </term>
- <term name="translator" form="short">trans.</term>
- <term name="interviewer" form="short">interviewer</term>
- <term name="circa" form="short">ca.</term>
- <term name="collection-editor" form="short">series ed.</term>
- </terms>
- </locale>
- <locale xml:lang="es">
- <terms>
- <term name="from">de</term>
- </terms>
- </locale>
- <locale xml:lang="de">
- <terms>
- <term name="et-al">et al.</term>
- </terms>
- </locale>
- <locale xml:lang="da">
- <terms>
- <term name="et-al">et al.</term>
- </terms>
- </locale>
- <locale xml:lang="nn">
- <terms>
- <term name="et-al">et al.</term>
- </terms>
- </locale>
- <locale xml:lang="nb">
- <terms>
- <term name="et-al">et al.</term>
- </terms>
- </locale>
- <macro name="container-contributors-booklike">
- <choose>
- <if variable="container-title">
- <names variable="editor translator" delimiter=", &amp; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=" (" text-case="title" suffix=")"/>
- <substitute>
- <names variable="editorial-director"/>
- <names variable="collection-editor"/>
- <names variable="container-author"/>
- </substitute>
- </names>
- </if>
- </choose>
- </macro>
- <macro name="container-contributors">
- <choose>
- <!-- book is here to catch software with container titles -->
- <if type="book broadcast chapter entry entry-dictionary entry-encyclopedia graphic map personal_communication report speech" match="any">
- <text macro="container-contributors-booklike"/>
- </if>
- <else-if type="paper-conference">
- <choose>
- <if variable="collection-editor container-author editor" match="any">
- <text macro="container-contributors-booklike"/>
- </if>
- </choose>
- </else-if>
- </choose>
- </macro>
- <macro name="secondary-contributors-booklike">
- <group delimiter="; ">
- <choose>
- <if variable="title">
- <names variable="interviewer">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </if>
- </choose>
- <choose>
- <if variable="container-title" match="none">
- <group delimiter="; ">
- <names variable="container-author">
- <label form="verb-short" suffix=" " text-case="title"/>
- <name and="symbol" initialize-with=". " delimiter=", "/>
- </names>
- <names variable="editor translator" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </group>
- </if>
- </choose>
- </group>
- </macro>
- <macro name="secondary-contributors">
- <choose>
- <!-- book is here to catch software with container titles -->
- <if type="book broadcast chapter entry entry-dictionary entry-encyclopedia graphic map report" match="any">
- <text macro="secondary-contributors-booklike"/>
- </if>
- <else-if type="personal_communication">
- <group delimiter="; ">
- <group delimiter=" ">
- <choose>
- <if variable="genre" match="any">
- <text variable="genre" text-case="capitalize-first"/>
- </if>
- <else>
- <text term="letter" text-case="capitalize-first"/>
- </else>
- </choose>
- <names variable="recipient" delimiter=", ">
- <label form="verb" suffix=" "/>
- <name and="symbol" delimiter=", "/>
- </names>
- </group>
- <text variable="medium" text-case="capitalize-first"/>
- <choose>
- <if variable="container-title" match="none">
- <names variable="editor translator" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </if>
- </choose>
- </group>
- </else-if>
- <else-if type="song">
- <choose>
- <if variable="original-author composer" match="any">
- <group delimiter="; ">
- <!-- Replace prefix with performer label as that becomes available -->
- <names variable="author" prefix="Recorded by ">
- <label form="verb" text-case="title"/>
- <name and="symbol" initialize-with=". " delimiter=", "/>
- </names>
- <names variable="translator">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </group>
- </if>
- </choose>
- </else-if>
- <else-if type="article-journal article-magazine article-newspaper" match="any">
- <group delimiter="; ">
- <choose>
- <if variable="title">
- <names variable="interviewer" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </if>
- </choose>
- <names variable="translator" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </group>
- </else-if>
- <else-if type="paper-conference">
- <choose>
- <if variable="collection-editor editor" match="any">
- <text macro="secondary-contributors-booklike"/>
- </if>
- <else>
- <group delimiter="; ">
- <choose>
- <if variable="title">
- <names variable="interviewer" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </if>
- </choose>
- <names variable="translator" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </group>
- </else>
- </choose>
- </else-if>
- <else>
- <group delimiter="; ">
- <choose>
- <if variable="title">
- <names variable="interviewer">
- <name and="symbol" initialize-with=". " delimiter="; "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </if>
- </choose>
- <names variable="editor translator" delimiter="; ">
- <name and="symbol" initialize-with=". " delimiter=", "/>
- <label form="short" prefix=", " text-case="title"/>
- </names>
- </group>
- </else>
- </choose>
- </macro>
- <macro name="author">
- <choose>
- <if type="song">
- <names variable="composer" delimiter=", ">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <substitute>
- <names variable="original-author"/>
- <names variable="author"/>
- <names variable="translator">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="short" prefix=" (" suffix=")" text-case="title"/>
- </names>
- <group delimiter=" ">
- <text macro="title"/>
- <text macro="description"/>
- <text macro="format"/>
- </group>
- </substitute>
- </names>
- </if>
- <else-if type="treaty"/>
- <else>
- <names variable="author" delimiter=", ">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <substitute>
- <names variable="illustrator"/>
- <names variable="composer"/>
- <names variable="director">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="long" prefix=" (" suffix=")" text-case="title"/>
- </names>
- <choose>
- <if variable="container-title">
- <choose>
- <if type="book entry entry-dictionary entry-encyclopedia">
- <text macro="title"/>
- </if>
- <else>
- <names variable="translator"/>
- </else>
- </choose>
- <names variable="translator">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="short" prefix=" (" suffix=")" text-case="title"/>
- </names>
- </if>
- </choose>
- <names variable="editor translator" delimiter=", ">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="short" prefix=" (" suffix=")" text-case="title"/>
- </names>
- <names variable="editorial-director">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="short" prefix=" (" suffix=")" text-case="title"/>
- </names>
- <names variable="collection-editor">
- <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>
- <label form="short" prefix=" (" suffix=")" text-case="title"/>
- </names>
- <choose>
- <if type="report">
- <text variable="publisher"/>
- </if>
- </choose>
- <group delimiter=" ">
- <text macro="title"/>
- <text macro="description"/>
- <text macro="format"/>
- </group>
- </substitute>
- </names>
- </else>
- </choose>
- </macro>
- <macro name="author-short">
- <choose>
- <if type="patent" variable="number" match="all">
- <text macro="patent-number"/>
- </if>
- <else-if type="treaty">
- <text variable="title" form="short"/>
- </else-if>
- <else-if type="personal_communication">
- <choose>
- <if variable="archive DOI publisher URL" match="none">
- <group delimiter=", ">
- <names variable="author">
- <name and="symbol" delimiter=", " initialize-with=". "/>
- <substitute>
- <text variable="title" form="short" quotes="true"/>
- </substitute>
- </names>
- <!-- This should be localized -->
- <text value="personal communication"/>
- </group>
- </if>
- <else>
- <names variable="author" delimiter=", ">
- <name form="short" and="symbol" delimiter=", " initialize-with=". "/>
- <substitute>
- <names variable="editor"/>
- <names variable="translator"/>
- <choose>
- <if variable="container-title">
- <text variable="title" form="short" quotes="true"/>
- </if>
- <else>
- <text variable="title" form="short" font-style="italic"/>
- </else>
- </choose>
- <text macro="format-short" prefix="[" suffix="]"/>
- </substitute>
- </names>
- </else>
- </choose>
- </else-if>
- <else-if type="song">
- <names variable="composer" delimiter=", ">
- <name form="short" and="symbol" delimiter=", " initialize-with=". "/>
- <substitute>
- <names variable="original-author"/>
- <names variable="author"/>
- <names variable="translator"/>
- <choose>
- <if variable="container-title">
- <text variable="title" form="short" quotes="true"/>
- </if>
- <else>
- <text variable="title" form="short" font-style="italic"/>
- </else>
- </choose>
- <text macro="format-short" prefix="[" suffix="]"/>
- </substitute>
- </names>
- </else-if>
- <else>
- <names variable="author" delimiter=", ">
- <name form="short" and="symbol" delimiter=", " initialize-with=". "/>
- <substitute>
- <names variable="illustrator"/>
- <names variable="composer"/>
- <names variable="director"/>
- <choose>
- <if variable="container-title">
- <choose>
- <if type="book entry entry-dictionary entry-encyclopedia">
- <text variable="title" form="short" quotes="true"/>
- </if>
- <else>
- <names variable="translator"/>
- </else>
- </choose>
- </if>
- </choose>
- <names variable="editor"/>
- <names variable="editorial-director"/>
- <names variable="translator"/>
- <choose>
- <if type="report" variable="publisher" match="all">
- <text variable="publisher"/>
- </if>
- <else-if type="legal_case">
- <text variable="title" font-style="italic"/>
- </else-if>
- <else-if type="bill legislation" match="any">
- <text variable="title" form="short"/>
- </else-if>
- <else-if variable="reviewed-author" type="review review-book" match="any">
- <text macro="format-short" prefix="[" suffix="]"/>
- </else-if>
- <else-if type="post post-weblog webpage" variable="container-title" match="any">
- <text variable="title" form="short" quotes="true"/>
- </else-if>
- <else>
- <text variable="title" form="short" font-style="italic"/>
- </else>
- </choose>
- <text macro="format-short" prefix="[" suffix="]"/>
- </substitute>
- </names>
- </else>
- </choose>
- </macro>
- <macro name="patent-number">
- <!-- authority: U.S. ; genre: patent ; number: 123,445 -->
- <group delimiter=" ">
- <text variable="authority"/>
- <choose>
- <if variable="genre">
- <text variable="genre" text-case="capitalize-first"/>
- </if>
- <else>
- <!-- This should be localized -->
- <text value="patent" text-case="capitalize-first"/>
- </else>
- </choose>
- <group delimiter=" ">
- <text term="issue" form="short" text-case="capitalize-first"/>
- <text variable="number"/>
- </group>
- </group>
- </macro>
- <macro name="access">
- <choose>
- <if type="bill legal_case legislation" match="any"/>
- <else-if variable="DOI" match="any">
- <text variable="DOI" prefix="https://doi.org/"/>
- </else-if>
- <else-if variable="URL">
- <group delimiter=" ">
- <text term="retrieved" text-case="capitalize-first"/>
- <choose>
- <if type="post post-weblog webpage" match="any">
- <date variable="accessed" form="text" suffix=","/>
- </if>
- </choose>
- <text term="from"/>
- <choose>
- <if type="report">
- <choose>
- <if variable="author editor translator" match="any">
- <!-- This should be localized -->
- <text variable="publisher" suffix=" website:"/>
- </if>
- </choose>
- </if>
- <else-if type="post post-weblog webpage" match="any">
- <!-- This should be localized -->
- <text variable="container-title" suffix=" website:"/>
- </else-if>
- </choose>
- <text variable="URL"/>
- </group>
- </else-if>
- <else-if variable="archive">
- <choose>
- <if type="article article-journal article-magazine article-newspaper dataset paper-conference report speech thesis" match="any">
- <!-- This section is for electronic database locations. Physical archives for these and other item types are called in 'publisher' macro -->
- <choose>
- <if variable="archive-place" match="none">
- <group delimiter=" ">
- <text term="retrieved" text-case="capitalize-first"/>
- <text term="from"/>
- <text variable="archive" suffix="."/>
- <text variable="archive_location" prefix="(" suffix=")"/>
- </group>
- </if>
- <else>
- <text macro="publisher" suffix="."/>
- </else>
- </choose>
- </if>
- <else>
- <text macro="publisher" suffix="."/>
- </else>
- </choose>
- </else-if>
- <else>
- <text macro="publisher" suffix="."/>
- </else>
- </choose>
- </macro>
- <macro name="title">
- <choose>
- <if type="treaty">
- <group delimiter=", ">
- <text variable="title" text-case="title"/>
- <names variable="author">
- <name initialize-with="." form="short" delimiter="-"/>
- </names>
- </group>
- </if>
- <else-if type="patent" variable="number" match="all">
- <text macro="patent-number" font-style="italic"/>
- </else-if>
- <else-if variable="title">
- <choose>
- <if variable="version" type="book" match="all">
- <!---This is a hack until we have a software type -->
- <text variable="title"/>
- </if>
- <else-if variable="reviewed-author reviewed-title" type="review review-book" match="any">
- <choose>
- <if variable="reviewed-title">
- <choose>
- <if type="post post-weblog webpage" variable="container-title" match="any">
- <text variable="title"/>
- </if>
- <else>
- <text variable="title" font-style="italic"/>
- </else>
- </choose>
- </if>
- </choose>
- </else-if>
- <else-if type="post post-weblog webpage" variable="container-title" match="any">
- <text variable="title"/>
- </else-if>
- <else>
- <text variable="title" font-style="italic"/>
- </else>
- </choose>
- </else-if>
- <else-if variable="interviewer" type="interview" match="any">
- <names variable="interviewer">
- <label form="verb-short" suffix=" " text-case="capitalize-first"/>
- <name and="symbol" initialize-with=". " delimiter=", "/>
- </names>
- </else-if>
- </choose>
- </macro>
- <!-- APA has four descriptive sections following the title: -->
- <!-- (description), [format], container, event -->
- <macro name="description">
- <group prefix="(" suffix=")">
- <choose>
- <!-- book is here to catch software with container titles -->
- <if type="book report" match="any">
- <choose>
- <if variable="container-title">
- <text macro="secondary-contributors"/>
- </if>
- <else>
- <group delimiter="; ">
- <text macro="description-report"/>
- <text macro="secondary-contributors"/>
- </group>
- </else>
- </choose>
- </if>
- <else-if type="thesis">
- <group delimiter="; ">
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <choose>
- <!-- In APA journals, the university of a thesis is always cited, even if another locator is given -->
- <if variable="DOI URL archive" match="any">
- <text variable="publisher"/>
- </if>
- </choose>
- </group>
- <text macro="locators"/>
- <text macro="secondary-contributors"/>
- </group>
- </else-if>
- <else-if type="book interview manuscript motion_picture musical_score pamphlet post-weblog speech webpage" match="any">
- <group delimiter="; ">
- <text macro="locators"/>
- <text macro="secondary-contributors"/>
- </group>
- </else-if>
- <else-if type="song">
- <choose>
- <if variable="container-title" match="none">
- <text macro="locators"/>
- </if>
- </choose>
- </else-if>
- <else-if type="article dataset figure" match="any">
- <choose>
- <if variable="container-title">
- <text macro="secondary-contributors"/>
- </if>
- <else>
- <group delimiter="; ">
- <text macro="locators"/>
- <text macro="secondary-contributors"/>
- </group>
- </else>
- </choose>
- </else-if>
- <else-if type="bill legislation legal_case patent treaty personal_communication" match="none">
- <text macro="secondary-contributors"/>
- </else-if>
- </choose>
- </group>
- </macro>
- <macro name="format">
- <group prefix="[" suffix="]">
- <choose>
- <if variable="reviewed-author reviewed-title" type="review review-book" match="any">
- <group delimiter=", ">
- <choose>
- <if variable="genre">
- <!-- Delimiting by , rather than "of" to avoid incorrect grammar -->
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <choose>
- <if variable="reviewed-title">
- <text variable="reviewed-title" font-style="italic"/>
- </if>
- <else>
- <!-- Assume `title` is title of reviewed work -->
- <text variable="title" font-style="italic"/>
- </else>
- </choose>
- </group>
- </if>
- <else>
- <!-- This should be localized -->
- <group delimiter=" ">
- <text value="Review of"/>
- <choose>
- <if variable="reviewed-title">
- <text variable="reviewed-title" font-style="italic"/>
- </if>
- <else>
- <!-- Assume `title` is title of reviewed work -->
- <text variable="title" font-style="italic"/>
- </else>
- </choose>
- </group>
- </else>
- </choose>
- <names variable="reviewed-author">
- <label form="verb-short" suffix=" "/>
- <name and="symbol" initialize-with=". " delimiter=", "/>
- </names>
- </group>
- </if>
- <else>
- <text macro="format-short"/>
- </else>
- </choose>
- </group>
- </macro>
- <macro name="format-short">
- <choose>
- <if variable="reviewed-author reviewed-title" type="review review-book" match="any">
- <choose>
- <if variable="reviewed-title" match="none">
- <choose>
- <if variable="genre">
- <!-- Delimiting by , rather than "of" to avoid incorrect grammar -->
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <text variable="title" form="short" font-style="italic"/>
- </group>
- </if>
- <else>
- <!-- This should be localized -->
- <group delimiter=" ">
- <text value="Review of"/>
- <text variable="title" form="short" font-style="italic"/>
- </group>
- </else>
- </choose>
- </if>
- <else>
- <text variable="title" form="short" quotes="true"/>
- </else>
- </choose>
- </if>
- <else-if type="speech thesis" match="any">
- <text variable="medium" text-case="capitalize-first"/>
- </else-if>
- <!-- book is here to catch software with container titles -->
- <else-if type="book report" match="any">
- <choose>
- <if variable="container-title" match="none">
- <text macro="format-report"/>
- </if>
- </choose>
- </else-if>
- <else-if type="manuscript pamphlet" match="any">
- <text variable="medium" text-case="capitalize-first"/>
- </else-if>
- <else-if type="personal_communication">
- <text macro="secondary-contributors"/>
- </else-if>
- <else-if type="song">
- <group delimiter="; ">
- <text macro="secondary-contributors"/>
- <choose>
- <if variable="container-title" match="none">
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <text variable="medium" text-case="capitalize-first"/>
- </group>
- </if>
- </choose>
- </group>
- </else-if>
- <else-if type="paper-conference">
- <group delimiter=", ">
- <choose>
- <if variable="collection-editor editor issue page volume" match="any">
- <text variable="genre" text-case="capitalize-first"/>
- </if>
- </choose>
- <text variable="medium" text-case="capitalize-first"/>
- </group>
- </else-if>
- <else-if type="bill legislation legal_case patent treaty" match="none">
- <choose>
- <if variable="genre medium" match="any">
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <text variable="medium" text-case="capitalize-first"/>
- </group>
- </if>
- <else-if type="dataset">
- <!-- This should be localized -->
- <text value="Data set"/>
- </else-if>
- </choose>
- </else-if>
- </choose>
- </macro>
- <macro name="description-report">
- <choose>
- <if variable="number">
- <group delimiter="; ">
- <group delimiter=" ">
- <text variable="genre" text-case="title"/>
- <!-- Replace with term="number" if that becomes available -->
- <text term="issue" form="short" text-case="capitalize-first"/>
- <text variable="number"/>
- </group>
- <text macro="locators"/>
- </group>
- </if>
- <else>
- <text macro="locators"/>
- </else>
- </choose>
- </macro>
- <macro name="format-report">
- <choose>
- <if variable="number">
- <text variable="medium" text-case="capitalize-first"/>
- </if>
- <else>
- <group delimiter=", ">
- <text variable="genre" text-case="capitalize-first"/>
- <text variable="medium" text-case="capitalize-first"/>
- </group>
- </else>
- </choose>
- </macro>
- <macro name="archive">
- <group delimiter=". ">
- <group delimiter=", ">
- <choose>
- <if type="manuscript">
- <text variable="genre"/>
- </if>
- </choose>
- <group delimiter=" ">
- <!-- Replace "archive" with "archive_collection" as that becomes available -->
- <text variable="archive"/>
- <text variable="archive_location" prefix="(" suffix=")"/>
- </group>
- </group>
- <group delimiter=", ">
- <!-- Move "archive" here when "archive_collection" becomes available -->
- <text variable="archive-place"/>
- </group>
- </group>
- </macro>
- <macro name="publisher">
- <choose>
- <if type="manuscript pamphlet" match="any">
- <choose>
- <if variable="archive archive_location archive-place" match="any">
- <group delimiter=". ">
- <group delimiter=": ">
- <text variable="publisher-place"/>
- <text variable="publisher"/>
- </group>
- <text macro="archive"/>
- </group>
- </if>
- <else>
- <group delimiter=", ">
- <text variable="genre"/>
- <text variable="publisher"/>
- <text variable="publisher-place"/>
- </group>
- </else>
- </choose>
- </if>
- <else-if type="thesis" match="any">
- <group delimiter=". ">
- <group delimiter=", ">
- <text variable="publisher"/>
- <text variable="publisher-place"/>
- </group>
- <text macro="archive"/>
- </group>
- </else-if>
- <else-if type="patent">
- <group delimiter=". ">
- <group delimiter=": ">
- <text variable="publisher-place"/>
- <text variable="publisher"/>
- </group>
- <text macro="archive"/>
- </group>
- </else-if>
- <else-if type="article-journal article-magazine article-newspaper" match="any">
- <text macro="archive"/>
- </else-if>
- <else-if type="post post-weblog webpage" match="none">
- <group delimiter=". ">
- <choose>
- <if variable="event">
- <choose>
- <!-- Only print publisher info if published in a proceedings -->
- <if variable="collection-editor editor issue page volume" match="any">
- <group delimiter=": ">
- <text variable="publisher-place"/>
- <text variable="publisher"/>
- </group>
- </if>
- </choose>
- </if>
- <else>
- <group delimiter=": ">
- <text variable="publisher-place"/>
- <text variable="publisher"/>
- </group>
- </else>
- </choose>
- <text macro="archive"/>
- </group>
- </else-if>
- </choose>
- </macro>
- <macro name="event">
- <choose>
- <if variable="event" type="speech paper-conference" match="any">
- <choose>
- <!-- Don't print event info if published in a proceedings -->
- <if variable="collection-editor editor issue page volume" match="none">
- <group delimiter=" ">
- <text variable="genre" text-case="capitalize-first"/>
- <group delimiter=" ">
- <choose>
- <if variable="genre">
- <text term="presented at"/>
- </if>
- <else>
- <text term="presented at" text-case="capitalize-first"/>
- </else>
- </choose>
- <group delimiter=", ">
- <text variable="event"/>
- <text variable="event-place"/>
- </group>
- </group>
- </group>
- </if>
- </choose>
- </if>
- </choose>
- </macro>
- <macro name="issued">
- <choose>
- <if type="bill legal_case legislation" match="any"/>
- <else-if variable="issued">
- <group>
- <date variable="issued">
- <date-part name="year"/>
- </date>
- <text variable="year-suffix"/>
- <choose>
- <if type="speech">
- <date variable="issued" delimiter=" ">
- <date-part prefix=", " name="month"/>
- </date>
- </if>
- <else-if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog treaty webpage" match="any">
- <date variable="issued">
- <date-part prefix=", " name="month"/>
- <date-part prefix=" " name="day"/>
- </date>
- </else-if>
- <else-if type="paper-conference">
- <choose>
- <if variable="container-title" match="none">
- <date variable="issued">
- <date-part prefix=", " name="month"/>
- <date-part prefix=" " name="day"/>
- </date>
- </if>
- </choose>
- </else-if>
- <!-- Only year: article-journal chapter entry entry-dictionary entry-encyclopedia dataset figure graphic motion_picture manuscript map musical_score paper-conference [published] patent report review review-book song thesis -->
- </choose>
- </group>
- </else-if>
- <else-if variable="status">
- <group>
- <text variable="status" text-case="lowercase"/>
- <text variable="year-suffix" prefix="-"/>
- </group>
- </else-if>
- <else>
- <group>
- <text term="no date" form="short"/>
- <text variable="year-suffix" prefix="-"/>
- </group>
- </else>
- </choose>
- </macro>
- <macro name="issued-sort">
- <choose>
- <if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog speech treaty webpage" match="any">
- <date variable="issued">
- <date-part name="year"/>
- <date-part name="month"/>
- <date-part name="day"/>
- </date>
- </if>
- <else>
- <date variable="issued">
- <date-part name="year"/>
- </date>
- </else>
- </choose>
- </macro>
- <macro name="issued-year">
- <group>
- <choose>
- <if type="personal_communication">
- <choose>
- <if variable="archive DOI publisher URL" match="none">
- <!-- These variables indicate that the letter is retrievable by the reader. If not, then use the APA in-text-only personal communication format -->
- <date variable="issued" form="text"/>
- </if>
- <else>
- <date variable="issued">
- <date-part name="year"/>
- </date>
- </else>
- </choose>
- </if>
- <else>
- <date variable="issued">
- <date-part name="year"/>
- </date>
- </else>
- </choose>
- <text variable="year-suffix"/>
- </group>
- </macro>
- <macro name="issued-citation">
- <choose>
- <if variable="issued">
- <group delimiter="/">
- <choose>
- <if is-uncertain-date="original-date">
- <group prefix="[" suffix="]" delimiter=" ">
- <text term="circa" form="short"/>
- <date variable="original-date">
- <date-part name="year"/>
- </date>
- </group>
- </if>
- <else>
- <date variable="original-date">
- <date-part name="year"/>
- </date>
- </else>
- </choose>
- <choose>
- <if is-uncertain-date="issued">
- <group prefix="[" suffix="]" delimiter=" ">
- <text term="circa" form="short"/>
- <text macro="issued-year"/>
- </group>
- </if>
- <else>
- <text macro="issued-year"/>
- </else>
- </choose>
- </group>
- </if>
- <else-if variable="status">
- <text variable="status" text-case="lowercase"/>
- <text variable="year-suffix" prefix="-"/>
- </else-if>
- <else>
- <text term="no date" form="short"/>
- <text variable="year-suffix" prefix="-"/>
- </else>
- </choose>
- </macro>
- <macro name="original-date">
- <choose>
- <if type="bill legal_case legislation" match="any"/>
- <else-if type="speech">
- <date variable="original-date" delimiter=" ">
- <date-part name="month"/>
- <date-part name="year"/>
- </date>
- </else-if>
- <else-if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog treaty webpage" match="any">
- <date variable="original-date" form="text"/>
- </else-if>
- <else>
- <date variable="original-date">
- <date-part name="year"/>
- </date>
- </else>
- </choose>
- </macro>
- <macro name="original-published">
- <!--This should be localized -->
- <choose>
- <if type="bill legal_case legislation" match="any"/>
- <else-if type="interview motion_picture song" match="any">
- <text value="Original work recorded"/>
- </else-if>
- <else-if type="broadcast">
- <text value="Original work broadcast"/>
- </else-if>
- <else>
- <text value="Original work published"/>
- </else>
- </choose>
- </macro>
- <macro name="edition">
- <choose>
- <if is-numeric="edition">
- <group delimiter=" ">
- <number variable="edition" form="ordinal"/>
- <text term="edition" form="short"/>
- </group>
- </if>
- <else>
- <text variable="edition"/>
- </else>
- </choose>
- </macro>
- <macro name="locators">
- <choose>
- <if type="article-journal article-magazine figure review review-book" match="any">
- <group delimiter=", ">
- <group>
- <text variable="volume" font-style="italic"/>
- <text variable="issue" prefix="(" suffix=")"/>
- </group>
- <text variable="page"/>
- </group>
- </if>
- <else-if type="article-newspaper">
- <group delimiter=" ">
- <label variable="page" form="short"/>
- <text variable="page"/>
- </group>
- </else-if>
- <else-if type="paper-conference">
- <choose>
- <if variable="collection-editor editor" match="any">
- <text macro="locators-booklike"/>
- </if>
- <else>
- <group delimiter=", ">
- <group>
- <text variable="volume" font-style="italic"/>
- <text variable="issue" prefix="(" suffix=")"/>
- </group>
- <text variable="page"/>
- </group>
- </else>
- </choose>
- </else-if>
- <else-if type="bill broadcast interview legal_case legislation patent post post-weblog speech treaty webpage" match="none">
- <text macro="locators-booklike"/>
- </else-if>
- </choose>
- </macro>
- <macro name="locators-booklike">
- <group delimiter=", ">
- <text macro="edition"/>
- <group delimiter=" ">
- <text term="version" text-case="capitalize-first"/>
- <text variable="version"/>
- </group>
- <choose>
- <if variable="volume" match="any">
- <choose>
- <if is-numeric="volume" match="none"/>
- <else-if variable="collection-title">
- <choose>
- <if variable="editor translator" match="none">
- <choose>
- <if variable="collection-number">
- <group>
- <text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
- <number variable="volume" form="numeric"/>
- </group>
- </if>
- </choose>
- </if>
- </choose>
- </else-if>
- <else>
- <group>
- <text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
- <number variable="volume" form="numeric"/>
- </group>
- </else>
- </choose>
- </if>
- <else>
- <group>
- <text term="volume" form="short" plural="true" text-case="capitalize-first" suffix=" "/>
- <number variable="number-of-volumes" form="numeric" prefix="1&#8211;"/>
- </group>
- </else>
- </choose>
- <group>
- <label variable="page" form="short" suffix=" "/>
- <text variable="page"/>
- </group>
- </group>
- </macro>
- <macro name="citation-locator">
- <group>
- <choose>
- <if locator="chapter">
- <label variable="locator" text-case="capitalize-first"/>
- </if>
- <else>
- <label variable="locator" form="short"/>
- </else>
- </choose>
- <text variable="locator" prefix=" "/>
- </group>
- </macro>
- <macro name="container">
- <choose>
- <if type="article article-journal article-magazine article-newspaper review review-book" match="any">
- <group delimiter=", ">
- <text macro="container-title"/>
- <text macro="locators"/>
- </group>
- <choose>
- <!--for advance online publication-->
- <if variable="issued">
- <choose>
- <if variable="page issue" match="none">
- <text variable="status" text-case="capitalize-first" prefix=". "/>
- </if>
- </choose>
- </if>
- </choose>
- </if>
- <else-if type="article dataset figure" match="any">
- <choose>
- <if variable="container-title">
- <group delimiter=", ">
- <text macro="container-title"/>
- <text macro="locators"/>
- </group>
- <choose>
- <!--for advance online publication-->
- <if variable="issued">
- <choose>
- <if variable="page issue" match="none">
- <text variable="status" text-case="capitalize-first" prefix=". "/>
- </if>
- </choose>
- </if>
- </choose>
- </if>
- </choose>
- </else-if>
- <!-- book is here to catch software with container titles -->
- <else-if type="book" variable="container-title" match="all">
- <group delimiter=" ">
- <text term="in" text-case="capitalize-first" suffix=" "/>
- <group delimiter=", ">
- <text macro="container-contributors"/>
- <group delimiter=" ">
- <text macro="container-title"/>
- <text macro="description-report" prefix="(" suffix=")"/>
- <text macro="format-report" prefix="[" suffix="]"/>
- </group>
- </group>
- </group>
- </else-if>
- <else-if type="report" variable="container-title" match="all">
- <group delimiter=" ">
- <text term="in" text-case="capitalize-first" suffix=" "/>
- <group delimiter=", ">
- <text macro="container-contributors"/>
- <group delimiter=" ">
- <text macro="container-title"/>
- <text macro="description-report" prefix="(" suffix=")"/>
- <text macro="format-report" prefix="[" suffix="]"/>
- </group>
- </group>
- </group>
- </else-if>
- <else-if type="song" variable="container-title" match="all">
- <group delimiter=" ">
- <text term="in" text-case="capitalize-first" suffix=" "/>
- <group delimiter=", ">
- <text macro="container-contributors"/>
- <group delimiter=" ">
- <text macro="container-title"/>
- <text macro="locators" prefix="(" suffix=")"/>
- <group delimiter=", " prefix="[" suffix="]">
- <text variable="genre" text-case="capitalize-first"/>
- <text variable="medium" text-case="capitalize-first"/>
- </group>
- </group>
- </group>
- </group>
- </else-if>
- <else-if type="paper-conference">
- <choose>
- <if variable="editor collection-editor container-author" match="any">
- <text macro="container-booklike"/>
- </if>
- <else>
- <group delimiter=", ">
- <text macro="container-title"/>
- <text macro="locators"/>
- </group>
- </else>
- </choose>
- </else-if>
- <else-if type="book">
- <choose>
- <!-- book and software should not cite collection-title, only container-title -->
- <if variable="container-title">
- <text macro="container-booklike"/>
- </if>
- </choose>
- </else-if>
- <else-if type="broadcast chapter entry entry-dictionary entry-encyclopedia graphic map speech" match="any">
- <text macro="container-booklike"/>
- </else-if>
- <else-if type="bill legal_case legislation treaty" match="any">
- <text macro="legal-cites"/>
- </else-if>
- </choose>
- </macro>
- <macro name="container-booklike">
- <choose>
- <if variable="container-title collection-title" match="any">
- <group delimiter=" ">
- <text term="in" text-case="capitalize-first"/>
- <group delimiter=", ">
- <text macro="container-contributors"/>
- <choose>
- <if variable="container-author editor translator" match="none">
- <group delimiter=". ">
- <group delimiter=": ">
- <text variable="collection-title" font-style="italic" text-case="title"/>
- <choose>
- <if variable="collection-title">
- <group delimiter=" ">
- <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/>
- <number variable="collection-number" font-style="italic" form="numeric"/>
- <choose>
- <if variable="collection-number" match="none">
- <number variable="volume" font-style="italic" form="numeric"/>
- </if>
- </choose>
- </group>
- </if>
- </choose>
- </group>
- <!-- Replace with volume-title as that becomes available -->
- <group delimiter=": ">
- <text macro="container-title"/>
- <choose>
- <if variable="collection-title" is-numeric="volume" match="none">
- <group delimiter=" ">
- <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/>
- <text variable="volume" font-style="italic"/>
- </group>
- </if>
- </choose>
- </group>
- </group>
- </if>
- <else>
- <!-- Replace with volume-title as that becomes available -->
- <group delimiter=": ">
- <text macro="container-title"/>
- <choose>
- <if is-numeric="volume" match="none">
- <group delimiter=" ">
- <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/>
- <text variable="volume" font-style="italic"/>
- </group>
- </if>
- </choose>
- </group>
- </else>
- </choose>
- </group>
- <group delimiter="; " prefix="(" suffix=")">
- <text macro="locators"/>
- <names variable="container-author">
- <label form="verb-short" suffix=" " text-case="title"/>
- <name and="symbol" initialize-with=". " delimiter=", "/>
- </names>
- </group>
- </group>
- </if>
- </choose>
- </macro>
- <macro name="container-title">
- <choose>
- <if type="article article-journal article-magazine article-newspaper dataset" match="any">
- <text variable="container-title" font-style="italic" text-case="title"/>
- </if>
- <else-if type="paper-conference speech">
- <choose>
- <if variable="collection-editor container-author editor" match="any">
- <text variable="container-title" font-style="italic"/>
- </if>
- <else>
- <text variable="container-title" font-style="italic" text-case="title"/>
- </else>
- </choose>
- </else-if>
- <else-if type="bill legal_case legislation post-weblog webpage" match="none">
- <text variable="container-title" font-style="italic"/>
- </else-if>
- </choose>
- </macro>
- <macro name="legal-cites">
- <choose>
- <if type="legal_case">
- <group prefix=", " delimiter=" ">
- <group delimiter=" ">
- <choose>
- <if variable="container-title">
- <text variable="volume"/>
- <text variable="container-title"/>
- <group delimiter=" ">
- <!--change to label variable="section" as that becomes available -->
- <text term="section" form="symbol"/>
- <text variable="section"/>
- </group>
- <text variable="page"/>
- </if>
- <else>
- <group delimiter=" ">
- <choose>
- <if is-numeric="number">
- <!-- Replace with term="number" if that becomes available -->
- <text term="issue" form="short" text-case="capitalize-first"/>
- </if>
- </choose>
- <text variable="number"/>
- </group>
- </else>
- </choose>
- </group>
- <group prefix="(" suffix=")" delimiter=" ">
- <text variable="authority"/>
- <choose>
- <if variable="container-title" match="any">
- <!--Only print year for cases published in reporters-->
- <date variable="issued" form="numeric" date-parts="year"/>
- </if>
- <else>
- <date variable="issued" form="text"/>
- </else>
- </choose>
- </group>
- </group>
- </if>
- <else-if type="bill legislation" match="any">
- <group prefix=", " delimiter=" ">
- <group delimiter=", ">
- <choose>
- <if variable="number">
- <!--There's a public law number-->
- <text variable="number" prefix="Pub. L. No. "/>
- <group delimiter=" ">
- <!--change to label variable="section" as that becomes available -->
- <text term="section" form="symbol"/>
- <text variable="section"/>
- </group>
- <group delimiter=" ">
- <text variable="volume"/>
- <text variable="container-title"/>
- <text variable="page-first"/>
- </group>
- </if>
- <else>
- <group delimiter=" ">
- <text variable="volume"/>
- <text variable="container-title"/>
- <!--change to label variable="section" as that becomes available -->
- <text term="section" form="symbol"/>
- <text variable="section"/>
- </group>
- </else>
- </choose>
- </group>
- <date variable="issued" prefix="(" suffix=")">
- <date-part name="year"/>
- </date>
- </group>
- </else-if>
- <else-if type="treaty">
- <group delimiter=" ">
- <number variable="volume"/>
- <text variable="container-title"/>
- <text variable="page"/>
- </group>
- </else-if>
- </choose>
- </macro>
- <citation et-al-min="6" et-al-use-first="1" et-al-subsequent-min="3" et-al-subsequent-use-first="1" disambiguate-add-year-suffix="true" disambiguate-add-names="true" disambiguate-add-givenname="true" collapse="year" givenname-disambiguation-rule="primary-name">
- <sort>
- <key macro="author" names-min="8" names-use-first="6"/>
- <key macro="issued-sort"/>
- </sort>
- <layout prefix="(" suffix=")" delimiter="; ">
- <group delimiter=", ">
- <text macro="author-short"/>
- <text macro="issued-citation"/>
- <text macro="citation-locator"/>
- </group>
- </layout>
- </citation>
- <bibliography hanging-indent="true" et-al-min="8" et-al-use-first="6" et-al-use-last="true" entry-spacing="0" line-spacing="2">
- <sort>
- <key macro="author"/>
- <key macro="issued-sort" sort="ascending"/>
- <key macro="title"/>
- </sort>
- <layout>
- <group suffix=".">
- <group delimiter=". ">
- <text macro="author"/>
- <choose>
- <if is-uncertain-date="issued">
- <group prefix=" [" suffix="]" delimiter=" ">
- <text term="circa" form="short"/>
- <text macro="issued"/>
- </group>
- </if>
- <else>
- <text macro="issued" prefix=" (" suffix=")"/>
- </else>
- </choose>
- <group delimiter=" ">
- <text macro="title"/>
- <choose>
- <if variable="title interviewer" type="interview" match="any">
- <group delimiter=" ">
- <text macro="description"/>
- <text macro="format"/>
- </group>
- </if>
- <else>
- <group delimiter=" ">
- <text macro="format"/>
- <text macro="description"/>
- </group>
- </else>
- </choose>
- </group>
- <text macro="container"/>
- </group>
- <text macro="event" prefix=". "/>
- </group>
- <text macro="access" prefix=" "/>
- <choose>
- <if is-uncertain-date="original-date">
- <group prefix=" [" suffix="]" delimiter=" ">
- <text macro="original-published"/>
- <text term="circa" form="short"/>
- <text macro="original-date"/>
- </group>
- </if>
- <else-if variable="original-date">
- <group prefix=" (" suffix=")" delimiter=" ">
- <text macro="original-published"/>
- <text macro="original-date"/>
- </group>
- </else-if>
- </choose>
- </layout>
- </bibliography>
-</style> \ No newline at end of file
diff --git a/support/texlab/src/citation/bibutils.rs b/support/texlab/src/citation/bibutils.rs
deleted file mode 100644
index 9931ba3f9f..0000000000
--- a/support/texlab/src/citation/bibutils.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-use std::{ffi::CString, fs, mem::MaybeUninit, path::Path};
-
-use bibutils_sys::{
- bibl, bibl_free, bibl_freeparams, bibl_init, bibl_initparams, bibl_read, bibl_write, fclose,
- fopen, param, BIBL_ADSABSOUT, BIBL_BIBLATEXIN, BIBL_BIBTEXIN, BIBL_BIBTEXOUT, BIBL_COPACIN,
- BIBL_EBIIN, BIBL_ENDNOTEIN, BIBL_ENDNOTEOUT, BIBL_ENDNOTEXMLIN, BIBL_ISIOUT, BIBL_MEDLINEIN,
- BIBL_MODSIN, BIBL_MODSOUT, BIBL_NBIBIN, BIBL_NBIBOUT, BIBL_OK, BIBL_RISIN, BIBL_RISOUT,
- BIBL_WORD2007OUT, BIBL_WORDIN, FILE,
-};
-use tempfile::tempdir;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-#[allow(unused)]
-pub enum InputFormat {
- Bibtex,
- Biblatex,
- Copac,
- Ebi,
- Endnote,
- EndnoteXml,
- Medline,
- Mods,
- Nbib,
- Ris,
- Word,
-}
-
-impl InputFormat {
- fn read_mode(self) -> u32 {
- match self {
- Self::Bibtex => BIBL_BIBTEXIN,
- Self::Biblatex => BIBL_BIBLATEXIN,
- Self::Copac => BIBL_COPACIN,
- Self::Ebi => BIBL_EBIIN,
- Self::Endnote => BIBL_ENDNOTEIN,
- Self::EndnoteXml => BIBL_ENDNOTEXMLIN,
- Self::Medline => BIBL_MEDLINEIN,
- Self::Mods => BIBL_MODSIN,
- Self::Nbib => BIBL_NBIBIN,
- Self::Ris => BIBL_RISIN,
- Self::Word => BIBL_WORDIN,
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-#[allow(unused)]
-pub enum OutputFormat {
- Adsabs,
- Bibtex,
- Endnote,
- Isi,
- Mods,
- Nbib,
- Ris,
- Word2007,
-}
-
-impl OutputFormat {
- fn write_mode(self) -> u32 {
- match self {
- Self::Adsabs => BIBL_ADSABSOUT,
- Self::Bibtex => BIBL_BIBTEXOUT,
- Self::Endnote => BIBL_ENDNOTEOUT,
- Self::Isi => BIBL_ISIOUT,
- Self::Mods => BIBL_MODSOUT,
- Self::Nbib => BIBL_NBIBOUT,
- Self::Ris => BIBL_RISOUT,
- Self::Word2007 => BIBL_WORD2007OUT,
- }
- }
-}
-
-struct Context {
- inner: MaybeUninit<bibl>,
-}
-
-impl Context {
- fn new() -> Self {
- let mut inner = MaybeUninit::zeroed();
- unsafe {
- bibl_init(inner.as_mut_ptr());
- }
- Self { inner }
- }
-}
-
-impl Drop for Context {
- fn drop(&mut self) {
- unsafe {
- bibl_free(self.inner.as_mut_ptr());
- }
- }
-}
-
-unsafe impl Send for Context {}
-
-struct Params {
- inner: MaybeUninit<param>,
-}
-
-impl Params {
- fn new(from: InputFormat, to: OutputFormat) -> Self {
- let program = CString::new("texlab").unwrap();
- let mut inner = MaybeUninit::zeroed();
- unsafe {
- bibl_initparams(
- inner.as_mut_ptr(),
- from.read_mode() as i32,
- to.write_mode() as i32,
- program.as_ptr() as *mut std::os::raw::c_char,
- );
- }
- Self { inner }
- }
-}
-
-impl Drop for Params {
- fn drop(&mut self) {
- unsafe {
- bibl_freeparams(self.inner.as_mut_ptr());
- }
- }
-}
-
-unsafe impl Send for Params {}
-
-struct File {
- path: CString,
- handle: *mut FILE,
-}
-
-impl File {
- fn new<M: Into<Vec<u8>>>(path: &Path, mode: M) -> Self {
- let path = CString::new(path.to_str().unwrap()).unwrap();
- let mode = CString::new(mode).unwrap();
- let handle = unsafe { fopen(path.as_ptr(), mode.as_ptr()) };
- Self { path, handle }
- }
-}
-
-impl Drop for File {
- fn drop(&mut self) {
- unsafe {
- fclose(self.handle);
- }
- }
-}
-
-unsafe impl Send for File {}
-
-pub fn convert(input: &str, from: InputFormat, to: OutputFormat) -> Option<String> {
- let mut context = Context::new();
- let mut params = Params::new(from, to);
- let dir = tempdir().expect("failed to create a temporary directory");
-
- let input_path = dir.path().join("input");
- fs::write(&input_path, input).ok()?;
- let input_file = File::new(&input_path, "r");
- unsafe {
- let status = bibl_read(
- context.inner.as_mut_ptr(),
- input_file.handle,
- input_file.path.as_ptr() as *mut std::os::raw::c_char,
- params.inner.as_mut_ptr(),
- );
-
- if status != BIBL_OK as i32 {
- return None;
- }
- }
-
- let output_path = dir.path().join("output");
- let output_file = File::new(&output_path, "w");
- unsafe {
- let status = bibl_write(
- context.inner.as_mut_ptr(),
- output_file.handle,
- params.inner.as_mut_ptr(),
- );
-
- if status != BIBL_OK as i32 {
- return None;
- }
- }
-
- // Remove BOM
- let data = fs::read(&output_path).ok()?;
- if data.is_empty() {
- return None;
- }
-
- let text = String::from_utf8_lossy(&data[3..]).into_owned();
- Some(text)
-}
diff --git a/support/texlab/src/citation/driver.rs b/support/texlab/src/citation/driver.rs
new file mode 100644
index 0000000000..eaf98304c4
--- /dev/null
+++ b/support/texlab/src/citation/driver.rs
@@ -0,0 +1,733 @@
+use isocountry::CountryCode;
+use itertools::Itertools;
+use titlecase::titlecase;
+use url::Url;
+
+use crate::syntax::bibtex;
+
+use super::{
+ entry::{EntryData, EntryKind},
+ field::{
+ author::AuthorField,
+ date::DateField,
+ number::{NumberField, NumberFieldData},
+ text::TextField,
+ },
+ output::{Inline, InlineBuilder, Punct},
+};
+
+#[derive(Debug, Default)]
+pub struct Driver {
+ builder: InlineBuilder,
+}
+
+impl Driver {
+ pub fn process(&mut self, entry: &bibtex::Entry) -> Option<()> {
+ let entry = EntryData::parse(entry)?;
+ match entry.kind {
+ EntryKind::Article
+ | EntryKind::DataSet
+ | EntryKind::Manual
+ | EntryKind::Misc
+ | EntryKind::Online
+ | EntryKind::Electronic
+ | EntryKind::Www
+ | EntryKind::Proceedings
+ | EntryKind::MVProceedings
+ | EntryKind::Set
+ | EntryKind::Software
+ | EntryKind::Thesis
+ | EntryKind::MasterThesis
+ | EntryKind::PhdThesis
+ | EntryKind::Unknown => self.article_like(entry),
+ EntryKind::Book
+ | EntryKind::MVBook
+ | EntryKind::InBook
+ | EntryKind::BookInBook
+ | EntryKind::SuppBook
+ | EntryKind::Booklet
+ | EntryKind::Collection
+ | EntryKind::MVCollection
+ | EntryKind::InCollection
+ | EntryKind::SuppCollection
+ | EntryKind::Periodical
+ | EntryKind::SuppPeriodical
+ | EntryKind::InProceedings
+ | EntryKind::Conference
+ | EntryKind::Reference
+ | EntryKind::MVReference
+ | EntryKind::InReference
+ | EntryKind::Report
+ | EntryKind::TechReport => self.book_like(entry),
+ EntryKind::Patent => self.patent(entry),
+ }
+ }
+
+ fn article_like(&mut self, mut entry: EntryData) -> Option<()> {
+ self.author(&mut entry);
+ self.title(&mut entry);
+ self.translator(&mut entry);
+ self.commentator(&mut entry);
+ self.annotator(&mut entry);
+ self.version(&mut entry);
+ self.journal(&mut entry);
+ self.article_series(&mut entry);
+
+ if let Some(volume) = entry.number.remove(&NumberField::Volume) {
+ let number = entry
+ .number
+ .remove(&NumberField::Number)
+ .map(|data| format!(".{}", data))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Regular(format!("{}{}", volume, number)),
+ Punct::Space,
+ Punct::Space,
+ );
+ }
+
+ self.eid(&mut entry);
+ self.issue_and_date(&mut entry);
+ self.issue_title(&mut entry);
+ self.editors(&mut entry);
+ self.note(&mut entry);
+ self.pages(&mut entry);
+ self.issn(&mut entry);
+ self.doi(&mut entry);
+ self.eprint(&mut entry);
+ self.url(&mut entry);
+ self.addendum(&mut entry);
+ self.pubstate(&mut entry);
+ Some(())
+ }
+
+ fn book_like(&mut self, mut entry: EntryData) -> Option<()> {
+ self.author(&mut entry);
+ self.title(&mut entry);
+ self.inbook_title(&mut entry);
+ self.main_title(&mut entry);
+ self.event(&mut entry);
+ self.editors(&mut entry);
+ self.translator(&mut entry);
+ self.commentator(&mut entry);
+ self.annotator(&mut entry);
+ self.introduction(&mut entry);
+ self.foreword(&mut entry);
+ self.afterword(&mut entry);
+ self.edition(&mut entry);
+ self.book_volume(&mut entry);
+ self.volumes(&mut entry);
+ self.book_series(&mut entry);
+ self.how_published(&mut entry);
+ self.note(&mut entry);
+ self.publisher(&mut entry);
+ self.date(&mut entry);
+ self.chapter(&mut entry);
+ self.eid(&mut entry);
+ self.pages(&mut entry);
+ self.page_total(&mut entry);
+ self.isbn(&mut entry);
+ self.doi(&mut entry);
+ self.eprint(&mut entry);
+ self.url(&mut entry);
+ self.addendum(&mut entry);
+ self.pubstate(&mut entry);
+ Some(())
+ }
+
+ fn patent(&mut self, mut entry: EntryData) -> Option<()> {
+ self.author(&mut entry);
+ self.title(&mut entry);
+ self.patent_number(&mut entry);
+ self.holder(&mut entry);
+ self.date(&mut entry);
+ self.doi(&mut entry);
+ self.eprint(&mut entry);
+ self.url(&mut entry);
+ self.addendum(&mut entry);
+ self.pubstate(&mut entry);
+ Some(())
+ }
+
+ fn author(&mut self, entry: &mut EntryData) -> Option<()> {
+ let author = entry.author.remove(&AuthorField::Author)?;
+ self.builder.push(
+ Inline::Regular(author.to_string()),
+ Punct::Nothing,
+ Punct::Colon,
+ );
+
+ Some(())
+ }
+
+ fn commentator(&mut self, entry: &mut EntryData) -> Option<()> {
+ let commentator = entry.author.remove(&AuthorField::Commentator)?;
+ self.builder.push(
+ Inline::Regular(format!("With a comment. by {}", commentator)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn translator(&mut self, entry: &mut EntryData) -> Option<()> {
+ let translator = entry.author.remove(&AuthorField::Translator)?;
+ self.builder.push(
+ Inline::Regular(format!("Trans. by {}", translator)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn annotator(&mut self, entry: &mut EntryData) -> Option<()> {
+ let annotator = entry.author.remove(&AuthorField::Annotator)?;
+ self.builder.push(
+ Inline::Regular(format!("With annots. by {}", annotator)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn editors(&mut self, entry: &mut EntryData) {
+ self.editor(entry, AuthorField::Editor, TextField::EditorType);
+ self.editor(entry, AuthorField::EditorA, TextField::EditorTypeA);
+ self.editor(entry, AuthorField::EditorB, TextField::EditorTypeB);
+ self.editor(entry, AuthorField::EditorC, TextField::EditorTypeC);
+ }
+
+ fn editor(
+ &mut self,
+ entry: &mut EntryData,
+ name_field: AuthorField,
+ type_field: TextField,
+ ) -> Option<()> {
+ let editor = entry.author.remove(&name_field)?;
+ let editor_type = entry
+ .text
+ .remove(&type_field)
+ .map(|data| data.text)
+ .unwrap_or_else(|| "Ed. by".to_string());
+
+ self.builder.push(
+ Inline::Regular(format!("{editor_type} {editor}")),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn title(&mut self, entry: &mut EntryData) -> Option<()> {
+ let title = entry.text.remove(&TextField::Title)?;
+ let subtitle = entry
+ .text
+ .remove(&TextField::Subtitle)
+ .map(|data| format!(". {}", data.text))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Quoted(format!("{}{}", title.text, subtitle)),
+ Punct::Nothing,
+ Punct::Dot,
+ );
+
+ let addon = entry.text.remove(&TextField::TitleAddon)?;
+ self.builder
+ .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn main_title(&mut self, entry: &mut EntryData) -> Option<()> {
+ let title = entry.text.remove(&TextField::MainTitle)?;
+ let subtitle = entry
+ .text
+ .remove(&TextField::MainSubtitle)
+ .map(|data| format!(". {}", data.text))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ let addon = entry.text.remove(&TextField::MainTitleAddon)?;
+ self.builder
+ .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn inbook_title(&mut self, entry: &mut EntryData) -> Option<()> {
+ let title = entry.text.remove(&TextField::BookTitle)?;
+ let subtitle = entry
+ .text
+ .remove(&TextField::BookSubtitle)
+ .map(|data| format!(". {}", data.text))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ let addon = entry.text.remove(&TextField::BookTitleAddon)?;
+ self.builder
+ .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn issue_title(&mut self, entry: &mut EntryData) -> Option<()> {
+ let issue = entry.text.remove(&TextField::IssueTitle)?;
+ let subtitle = entry
+ .text
+ .remove(&TextField::IssueSubtitle)
+ .map(|data| format!(" {}", data.text))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Italic(titlecase(&format!("{}{}", issue.text, subtitle))),
+ Punct::Colon,
+ Punct::Space,
+ );
+
+ if let Some(addon) = entry.text.remove(&TextField::IssueTitleAddon) {
+ self.builder
+ .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot);
+ }
+
+ Some(())
+ }
+
+ fn event(&mut self, entry: &mut EntryData) -> Option<()> {
+ let title = entry.text.remove(&TextField::EventTitle)?;
+ let addon = entry
+ .text
+ .remove(&TextField::EventTitleAddon)
+ .map_or(String::new(), |addon| format!(". {}", addon.text));
+
+ let venue = entry.text.remove(&TextField::Venue);
+ let date = entry.date.remove(&DateField::EventDate);
+ let venue_and_date = match (venue, date) {
+ (None, None) => String::new(),
+ (None, Some(date)) => format!(" ({date})"),
+ (Some(venue), None) => format!(" ({})", venue.text),
+ (Some(venue), Some(date)) => format!(" ({}, {})", venue.text, date),
+ };
+
+ self.builder.push(
+ Inline::Regular(format!("{}{}{}", title.text, addon, venue_and_date)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn patent_number(&mut self, entry: &mut EntryData) -> Option<()> {
+ let number = entry.number.remove(&NumberField::Number)?;
+
+ let location = entry
+ .text
+ .remove(&TextField::Location)
+ .map_or(String::new(), |location| {
+ let text = location
+ .text
+ .split_whitespace()
+ .map(|word| {
+ if word == "countryuk" {
+ "United Kingdom"
+ } else {
+ word.strip_prefix("country")
+ .and_then(|code| CountryCode::for_alpha2_caseless(code).ok())
+ .map_or(word, |country| country.name())
+ }
+ })
+ .join(" ");
+
+ format!(" ({})", text)
+ });
+
+ self.builder.push(
+ Inline::Regular(format!("{}{}", number, location)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn holder(&mut self, entry: &mut EntryData) -> Option<()> {
+ let holder = entry.text.remove(&TextField::Holder)?;
+ self.builder
+ .push(Inline::Regular(holder.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn version(&mut self, entry: &mut EntryData) -> Option<()> {
+ let version = entry.text.remove(&TextField::Version)?;
+ self.builder.push(
+ Inline::Regular(format!("Version {}", version.text)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn article_series(&mut self, entry: &mut EntryData) -> Option<()> {
+ let series = entry.text.remove(&TextField::Series)?;
+ self.builder
+ .push(Inline::Regular(series.text), Punct::Space, Punct::Space);
+
+ Some(())
+ }
+
+ fn issue_and_date(&mut self, entry: &mut EntryData) -> Option<()> {
+ let date = [DateField::Date, DateField::Year, DateField::Month]
+ .iter()
+ .filter_map(|field| entry.date.remove(field))
+ .reduce(|a, b| a + b)?;
+
+ let issue = entry
+ .text
+ .remove(&TextField::Issue)
+ .map(|data| format!("{} ", data.text))
+ .unwrap_or_default();
+
+ self.builder.push(
+ Inline::Regular(format!("({issue}{date})")),
+ Punct::Space,
+ Punct::Colon,
+ );
+
+ Some(())
+ }
+
+ fn chapter(&mut self, entry: &mut EntryData) -> Option<()> {
+ let chapter = entry.text.remove(&TextField::Chapter)?;
+ self.builder.push(
+ Inline::Regular(format!("Chap. {}", chapter.text)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn date(&mut self, entry: &mut EntryData) -> Option<()> {
+ let date = [DateField::Date, DateField::Year, DateField::Month]
+ .iter()
+ .filter_map(|field| entry.date.remove(field))
+ .reduce(|a, b| a + b)?;
+
+ self.builder
+ .push(Inline::Regular(format!("{date}")), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn how_published(&mut self, entry: &mut EntryData) -> Option<()> {
+ let how_published = entry.text.remove(&TextField::HowPublished)?;
+ self.builder
+ .push(Inline::Regular(how_published.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn publisher(&mut self, entry: &mut EntryData) -> Option<()> {
+ let publisher = entry.text.remove(&TextField::Publisher)?.text;
+ let location = entry
+ .text
+ .remove(&TextField::Location)
+ .map_or(String::new(), |location| format!("{}: ", location.text));
+
+ self.builder.push(
+ Inline::Regular(format!("{location}{publisher}")),
+ Punct::Dot,
+ Punct::Comma,
+ );
+
+ Some(())
+ }
+
+ fn book_series(&mut self, entry: &mut EntryData) -> Option<()> {
+ let series = entry.text.remove(&TextField::Series)?;
+ let number = entry
+ .number
+ .remove(&NumberField::Number)
+ .map_or(String::new(), |number| format!(" {}", number));
+
+ self.builder.push(
+ Inline::Regular(format!("{}{}", series.text, number)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn volumes(&mut self, entry: &mut EntryData) -> Option<()> {
+ let volumes = entry.number.remove(&NumberField::Volume)?;
+ self.builder.push(
+ Inline::Regular(format!("{volumes} vols")),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn book_volume(&mut self, entry: &mut EntryData) -> Option<()> {
+ let volume = entry.number.remove(&NumberField::Volume)?;
+ let part = entry
+ .number
+ .remove(&NumberField::Part)
+ .map_or(String::new(), |part| format!(".{part}"));
+
+ self.builder.push(
+ Inline::Regular(format!("Vol. {}{}", volume, part)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn pubstate(&mut self, entry: &mut EntryData) -> Option<()> {
+ let pubstate = entry.text.remove(&TextField::Pubstate)?;
+ self.builder
+ .push(Inline::Regular(pubstate.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn addendum(&mut self, entry: &mut EntryData) -> Option<()> {
+ let addendum = entry.text.remove(&TextField::Addendum)?;
+ self.builder
+ .push(Inline::Regular(addendum.text), Punct::Dot, Punct::Dot);
+
+ Some(())
+ }
+
+ fn edition(&mut self, entry: &mut EntryData) -> Option<()> {
+ let text = match entry.number.remove(&NumberField::Edition)? {
+ NumberFieldData::Scalar(1) => "1st".to_string(),
+ NumberFieldData::Scalar(2) => "2nd".to_string(),
+ NumberFieldData::Scalar(3) => "3rd".to_string(),
+ NumberFieldData::Scalar(number) => format!("{}th", number),
+ NumberFieldData::Range(_, _) => return None,
+ NumberFieldData::Other(text) => text,
+ };
+
+ self.builder
+ .push(Inline::Regular(text), Punct::Dot, Punct::Dot);
+ Some(())
+ }
+
+ fn introduction(&mut self, entry: &mut EntryData) -> Option<()> {
+ let author = entry.author.remove(&AuthorField::Introduction)?;
+ self.builder.push(
+ Inline::Regular(format!("With an intro. by {}", author)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn foreword(&mut self, entry: &mut EntryData) -> Option<()> {
+ let author = entry.author.remove(&AuthorField::Commentator)?;
+ self.builder.push(
+ Inline::Regular(format!("With a forew. by {}", author)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn afterword(&mut self, entry: &mut EntryData) -> Option<()> {
+ let author = entry.author.remove(&AuthorField::Commentator)?;
+ self.builder.push(
+ Inline::Regular(format!("With an afterw. by {}", author)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn note(&mut self, entry: &mut EntryData) -> Option<()> {
+ let note = entry.text.remove(&TextField::Note)?;
+ self.builder
+ .push(Inline::Regular(note.text), Punct::Dot, Punct::Comma);
+
+ Some(())
+ }
+
+ fn pages(&mut self, entry: &mut EntryData) -> Option<()> {
+ let pages = entry.number.remove(&NumberField::Pages)?;
+ self.builder.push(
+ Inline::Regular(pages.to_string()),
+ Punct::Comma,
+ Punct::Space,
+ );
+
+ Some(())
+ }
+
+ fn page_total(&mut self, entry: &mut EntryData) -> Option<()> {
+ let page_total = entry.number.remove(&NumberField::PageTotal)?;
+ self.builder.push(
+ Inline::Regular(format!("{} pp", page_total)),
+ Punct::Dot,
+ Punct::Dot,
+ );
+
+ Some(())
+ }
+
+ fn journal(&mut self, entry: &mut EntryData) -> Option<()> {
+ let title = entry
+ .text
+ .remove(&TextField::Journal)
+ .or_else(|| entry.text.remove(&TextField::JournalTitle))?;
+
+ let subtitle = entry
+ .text
+ .remove(&TextField::JournalSubtitle)
+ .map_or(String::new(), |data| format!(" {}", data.text));
+
+ self.builder.push(
+ Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))),
+ Punct::Dot,
+ Punct::Space,
+ );
+
+ Some(())
+ }
+
+ fn eid(&mut self, entry: &mut EntryData) -> Option<()> {
+ let eid = entry.text.remove(&TextField::Eid)?;
+ self.builder
+ .push(Inline::Regular(eid.text), Punct::Comma, Punct::Space);
+
+ Some(())
+ }
+
+ fn isbn(&mut self, entry: &mut EntryData) -> Option<()> {
+ let isbn = entry.text.remove(&TextField::Isbn)?;
+ self.builder.push(
+ Inline::Regular("ISBN".to_string()),
+ Punct::Dot,
+ Punct::Colon,
+ );
+
+ self.builder
+ .push(Inline::Regular(isbn.text), Punct::Space, Punct::Dot);
+
+ Some(())
+ }
+
+ fn issn(&mut self, entry: &mut EntryData) -> Option<()> {
+ let issn = entry.text.remove(&TextField::Issn)?;
+ self.builder.push(
+ Inline::Regular("ISSN".to_string()),
+ Punct::Dot,
+ Punct::Colon,
+ );
+
+ self.builder
+ .push(Inline::Regular(issn.text), Punct::Space, Punct::Dot);
+
+ Some(())
+ }
+
+ fn url(&mut self, entry: &mut EntryData) -> Option<()> {
+ let url = entry.text.remove(&TextField::Url)?;
+
+ self.builder
+ .push(Inline::Regular("URL".to_string()), Punct::Dot, Punct::Colon);
+
+ let url = url.text;
+ let alt = url.clone();
+
+ self.builder
+ .push(Inline::Link { url, alt }, Punct::Space, Punct::Space);
+
+ let date = entry.date.remove(&DateField::UrlDate)?;
+ self.builder.push(
+ Inline::Regular(format!("({date})")),
+ Punct::Space,
+ Punct::Space,
+ );
+
+ Some(())
+ }
+
+ fn doi(&mut self, entry: &mut EntryData) -> Option<()> {
+ let doi = entry.text.remove(&TextField::Doi)?;
+ self.builder
+ .push(Inline::Regular("DOI".to_string()), Punct::Dot, Punct::Colon);
+
+ let alt = Url::parse(&doi.text)
+ .ok()
+ .filter(|url| !url.cannot_be_a_base())
+ .map_or(doi.text, |url| url.path()[1..].to_string());
+
+ let url = format!("https://doi.org/{alt}");
+
+ self.builder
+ .push(Inline::Link { url, alt }, Punct::Space, Punct::Dot);
+
+ Some(())
+ }
+
+ fn eprint(&mut self, entry: &mut EntryData) -> Option<()> {
+ let eprint = entry.text.remove(&TextField::Eprint)?;
+ let eprint_type = entry
+ .text
+ .remove(&TextField::EprintType)
+ .map_or_else(|| "eprint".to_string(), |data| data.text);
+
+ if eprint_type.eq_ignore_ascii_case("arxiv") {
+ self.builder.push(
+ Inline::Regular("arXiv".to_string()),
+ Punct::Dot,
+ Punct::Colon,
+ );
+
+ self.builder.push(
+ Inline::Link {
+ url: format!("https://arxiv.org/abs/{}", eprint.text),
+ alt: eprint.text,
+ },
+ Punct::Space,
+ Punct::Dot,
+ );
+ } else {
+ self.builder
+ .push(Inline::Regular(eprint_type), Punct::Dot, Punct::Colon);
+
+ self.builder
+ .push(Inline::Regular(eprint.text), Punct::Space, Punct::Dot);
+ }
+
+ Some(())
+ }
+
+ pub fn finish(self) -> impl Iterator<Item = (Inline, Punct)> {
+ self.builder.finish()
+ }
+}
diff --git a/support/texlab/src/citation/entry.rs b/support/texlab/src/citation/entry.rs
new file mode 100644
index 0000000000..c8b3f98753
--- /dev/null
+++ b/support/texlab/src/citation/entry.rs
@@ -0,0 +1,123 @@
+use std::str::FromStr;
+
+use rustc_hash::FxHashMap;
+use strum::EnumString;
+
+use crate::syntax::bibtex::{Entry, Field, HasName, HasType};
+
+use super::field::{
+ author::{AuthorField, AuthorFieldData},
+ date::{DateField, DateFieldData},
+ number::{NumberField, NumberFieldData},
+ text::{TextField, TextFieldData},
+};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
+#[strum(ascii_case_insensitive)]
+pub enum EntryKind {
+ Article,
+ Book,
+ MVBook,
+ InBook,
+ BookInBook,
+ SuppBook,
+ Booklet,
+ Collection,
+ MVCollection,
+ InCollection,
+ SuppCollection,
+ DataSet,
+ Manual,
+ Misc,
+ Online,
+ Electronic,
+ Www,
+ Patent,
+ Periodical,
+ SuppPeriodical,
+ Proceedings,
+ MVProceedings,
+ InProceedings,
+ Conference,
+ Reference,
+ MVReference,
+ InReference,
+ Report,
+ Set,
+ Software,
+ Thesis,
+ MasterThesis,
+ PhdThesis,
+ TechReport,
+ Unknown,
+}
+
+impl Default for EntryKind {
+ fn default() -> Self {
+ Self::Unknown
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct EntryData {
+ pub kind: EntryKind,
+ pub text: FxHashMap<TextField, TextFieldData>,
+ pub author: FxHashMap<AuthorField, AuthorFieldData>,
+ pub date: FxHashMap<DateField, DateFieldData>,
+ pub number: FxHashMap<NumberField, NumberFieldData>,
+}
+
+impl EntryData {
+ pub fn parse(entry: &Entry) -> Option<Self> {
+ let mut data = EntryData {
+ kind: entry
+ .type_token()
+ .and_then(|token| EntryKind::from_str(&token.text()[1..]).ok())
+ .unwrap_or(EntryKind::Unknown),
+ ..EntryData::default()
+ };
+
+ for field in entry.fields() {
+ let _ = data.parse_field(&field);
+ }
+
+ Some(data)
+ }
+
+ fn parse_field(&mut self, field: &Field) -> Option<()> {
+ let name = field.name_token()?;
+ let name = name.text();
+ self.parse_author_field(field, name)
+ .or_else(|| self.parse_date_field(field, name))
+ .or_else(|| self.parse_number_field(field, name))
+ .or_else(|| self.parse_text_field(field, name))
+ }
+
+ fn parse_author_field(&mut self, field: &Field, name: &str) -> Option<()> {
+ let name = AuthorField::parse(name)?;
+ let data = AuthorFieldData::parse(field)?;
+ self.author.insert(name, data);
+ Some(())
+ }
+
+ fn parse_date_field(&mut self, field: &Field, name: &str) -> Option<()> {
+ let name = DateField::parse(name)?;
+ let data = DateFieldData::parse(field)?;
+ self.date.insert(name, data);
+ Some(())
+ }
+
+ fn parse_number_field(&mut self, field: &Field, name: &str) -> Option<()> {
+ let name = NumberField::parse(name)?;
+ let data = NumberFieldData::parse(field)?;
+ self.number.insert(name, data);
+ Some(())
+ }
+
+ fn parse_text_field(&mut self, field: &Field, name: &str) -> Option<()> {
+ let name = TextField::parse(name).unwrap_or(TextField::Unknown);
+ let data = TextFieldData::parse(field)?;
+ self.text.insert(name, data);
+ Some(())
+ }
+}
diff --git a/support/texlab/src/citation/field.rs b/support/texlab/src/citation/field.rs
new file mode 100644
index 0000000000..d9f3adacab
--- /dev/null
+++ b/support/texlab/src/citation/field.rs
@@ -0,0 +1,4 @@
+pub mod author;
+pub mod date;
+pub mod number;
+pub mod text;
diff --git a/support/texlab/src/citation/field/author.rs b/support/texlab/src/citation/field/author.rs
new file mode 100644
index 0000000000..a5e4368320
--- /dev/null
+++ b/support/texlab/src/citation/field/author.rs
@@ -0,0 +1,73 @@
+use std::{borrow::Cow, fmt, str::FromStr};
+
+use human_name::Name;
+use itertools::Itertools;
+use strum::EnumString;
+
+use crate::syntax::bibtex::Field;
+
+use super::text::TextFieldData;
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
+#[strum(ascii_case_insensitive)]
+pub enum AuthorField {
+ Afterword,
+ Annotator,
+ Author,
+ Commentator,
+ Editor,
+ EditorA,
+ EditorB,
+ EditorC,
+ Foreword,
+ Introduction,
+ Translator,
+}
+
+impl AuthorField {
+ pub fn parse(input: &str) -> Option<Self> {
+ Self::from_str(input).ok()
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct AuthorFieldData {
+ pub authors: Vec<Name>,
+}
+
+impl fmt::Display for AuthorFieldData {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let names = self
+ .authors
+ .iter()
+ .map(|author| author.display_initial_surname());
+
+ for part in Itertools::intersperse(names, Cow::Borrowed(", ")) {
+ write!(f, "{}", part)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl AuthorFieldData {
+ pub fn parse(field: &Field) -> Option<Self> {
+ let TextFieldData { text } = TextFieldData::parse(field)?;
+ let mut authors = Vec::new();
+ let mut words = Vec::new();
+ for word in text.split_whitespace() {
+ if word.eq_ignore_ascii_case("and") {
+ authors.push(Name::parse(&words.join(" "))?);
+ words.clear();
+ } else {
+ words.push(word);
+ }
+ }
+
+ if !words.is_empty() {
+ authors.push(Name::parse(&words.join(" "))?);
+ }
+
+ Some(Self { authors })
+ }
+}
diff --git a/support/texlab/src/citation/field/date.rs b/support/texlab/src/citation/field/date.rs
new file mode 100644
index 0000000000..b9b3dbb438
--- /dev/null
+++ b/support/texlab/src/citation/field/date.rs
@@ -0,0 +1,81 @@
+use std::{fmt, ops::Add, str::FromStr};
+
+use chrono::{Datelike, Month, NaiveDate};
+use strum::EnumString;
+
+use crate::syntax::bibtex::Field;
+
+use super::text::TextFieldData;
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
+#[strum(ascii_case_insensitive)]
+pub enum DateField {
+ Date,
+ EventDate,
+ Month,
+ UrlDate,
+ Year,
+}
+
+impl DateField {
+ pub fn parse(input: &str) -> Option<Self> {
+ Self::from_str(input).ok()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub enum DateFieldData {
+ Date(NaiveDate),
+ Year(i32),
+ Month(Month),
+ Other(String),
+}
+
+impl Add for DateFieldData {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ match (self, rhs) {
+ (date @ Self::Year(_), Self::Year(_))
+ | (date @ Self::Month(_), Self::Month(_))
+ | (date @ Self::Date(_), Self::Date(_))
+ | (Self::Other(_), date)
+ | (date, Self::Other(_)) => date,
+ (Self::Year(year), Self::Month(month)) | (Self::Month(month), Self::Year(year)) => {
+ let new_date = NaiveDate::from_ymd(year, month.number_from_month(), 1);
+ Self::Date(new_date)
+ }
+ (Self::Year(year), Self::Date(date)) | (Self::Date(date), Self::Year(year)) => {
+ let new_date = date.with_year(year).unwrap_or(date);
+ Self::Date(new_date)
+ }
+ (Self::Date(date), Self::Month(month)) | (Self::Month(month), Self::Date(date)) => {
+ let new_date = date.with_month(month.number_from_month()).unwrap_or(date);
+ Self::Date(new_date)
+ }
+ }
+ }
+}
+
+impl fmt::Display for DateFieldData {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Year(year) => write!(f, "{}", year),
+ Self::Date(date) => write!(f, "{}", date.format("%b. %Y")),
+ Self::Month(month) => write!(f, "{}", month.name()),
+ Self::Other(text) => write!(f, "{}", text),
+ }
+ }
+}
+
+impl DateFieldData {
+ pub fn parse(field: &Field) -> Option<Self> {
+ let TextFieldData { text } = TextFieldData::parse(field)?;
+ NaiveDate::from_str(&text)
+ .ok()
+ .map(Self::Date)
+ .or_else(|| text.parse().ok().map(Self::Year))
+ .or_else(|| text.parse().ok().map(Self::Month))
+ .or(Some(Self::Other(text)))
+ }
+}
diff --git a/support/texlab/src/citation/field/number.rs b/support/texlab/src/citation/field/number.rs
new file mode 100644
index 0000000000..9f51ccef90
--- /dev/null
+++ b/support/texlab/src/citation/field/number.rs
@@ -0,0 +1,54 @@
+use std::{fmt, str::FromStr};
+
+use strum::EnumString;
+
+use crate::syntax::bibtex::Field;
+
+use super::text::TextFieldData;
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
+#[strum(ascii_case_insensitive)]
+pub enum NumberField {
+ Edition,
+ Number,
+ Pages,
+ PageTotal,
+ Part,
+ Volume,
+ Volumes,
+}
+
+impl NumberField {
+ pub fn parse(input: &str) -> Option<Self> {
+ Self::from_str(input).ok()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub enum NumberFieldData {
+ Scalar(u32),
+ Range(u32, u32),
+ Other(String),
+}
+
+impl fmt::Display for NumberFieldData {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Scalar(value) => write!(f, "{}", value),
+ Self::Range(start, end) => write!(f, "{}-{}", start, end),
+ Self::Other(value) => write!(f, "{}", value.replace("--", "-")),
+ }
+ }
+}
+
+impl NumberFieldData {
+ pub fn parse(field: &Field) -> Option<Self> {
+ let TextFieldData { text } = TextFieldData::parse(field)?;
+ text.split_once("--")
+ .or_else(|| text.split_once('-'))
+ .and_then(|(a, b)| Some((a.parse().ok()?, b.parse().ok()?)))
+ .map(|(a, b)| Self::Range(a, b))
+ .or_else(|| text.parse().ok().map(Self::Scalar))
+ .or(Some(Self::Other(text)))
+ }
+}
diff --git a/support/texlab/src/citation/field/text.rs b/support/texlab/src/citation/field/text.rs
new file mode 100644
index 0000000000..87119bd47c
--- /dev/null
+++ b/support/texlab/src/citation/field/text.rs
@@ -0,0 +1,263 @@
+use std::str::FromStr;
+
+use rowan::{ast::AstNode, NodeOrToken};
+use rustc_hash::FxHashSet;
+use strum::EnumString;
+
+use crate::syntax::bibtex::{
+ Accent, Command, CurlyGroup, Field, HasAccentName, HasCommandName, HasName, HasValue, HasWord,
+ Join, Literal, QuoteGroup, Root, SyntaxKind::*, SyntaxToken, Value,
+};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
+#[strum(ascii_case_insensitive)]
+pub enum TextField {
+ Abstract,
+ Addendum,
+ BookSubtitle,
+ BookTitle,
+ BookTitleAddon,
+ Chapter,
+ Doi,
+ EditorType,
+ EditorTypeA,
+ EditorTypeB,
+ EditorTypeC,
+ Eid,
+ Eprint,
+ EprintClass,
+ EprintType,
+ EventTitle,
+ EventTitleAddon,
+ Holder,
+ HowPublished,
+ Isbn,
+ Issn,
+ Issue,
+ IssueSubtitle,
+ IssueTitle,
+ IssueTitleAddon,
+ Journal,
+ JournalSubtitle,
+ JournalTitle,
+ JournalTitleAddon,
+ Language,
+ Location,
+ MainTitle,
+ MainSubtitle,
+ MainTitleAddon,
+ Note,
+ OrigLanguage,
+ Publisher,
+ Pubstate,
+ Series,
+ Subtitle,
+ Title,
+ TitleAddon,
+ Type,
+ Unknown,
+ Url,
+ Venue,
+ Version,
+}
+
+impl TextField {
+ pub fn parse(input: &str) -> Option<Self> {
+ Self::from_str(input).ok()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Default)]
+pub struct TextFieldData {
+ pub text: String,
+}
+
+impl TextFieldData {
+ pub fn parse(field: &Field) -> Option<Self> {
+ let mut builder = TextFieldDataBuilder::default();
+ builder.visit_value(&field.value()?)?;
+ Some(builder.data)
+ }
+}
+
+#[derive(Default)]
+struct TextFieldDataBuilder {
+ data: TextFieldData,
+ string_stack: FxHashSet<String>,
+}
+
+impl TextFieldDataBuilder {
+ fn visit_value(&mut self, value: &Value) -> Option<()> {
+ match value {
+ Value::Literal(lit) => {
+ self.visit_literal(lit)?;
+ }
+ Value::CurlyGroup(group) => {
+ self.visit_curly_group(group)?;
+ }
+ Value::QuoteGroup(group) => {
+ self.visit_quote_group(group)?;
+ }
+ Value::Join(join) => {
+ self.visit_join(join)?;
+ }
+ Value::Accent(accent) => {
+ let _ = self.visit_accent(accent);
+ }
+ Value::Command(command) => {
+ let _ = self.visit_command(command);
+ }
+ };
+
+ Some(())
+ }
+
+ fn visit_literal(&mut self, lit: &Literal) -> Option<()> {
+ if lit
+ .name_token()
+ .and_then(|name| self.visit_string_reference(&name))
+ .is_none()
+ {
+ lit.syntax()
+ .text()
+ .for_each_chunk(|text| self.data.text.push_str(text));
+ }
+
+ Some(())
+ }
+
+ fn visit_string_reference(&mut self, name: &SyntaxToken) -> Option<()> {
+ let root = Root::cast(name.ancestors().last()?)?;
+ let name = name.text();
+
+ let value = root
+ .strings()
+ .filter(|string| {
+ string
+ .name_token()
+ .map_or(false, |token| token.text() == name)
+ })
+ .find_map(|string| string.value())?;
+
+ if !self.string_stack.insert(name.to_string()) {
+ return None;
+ }
+
+ let _ = self.visit_value(&value);
+ self.string_stack.remove(name);
+ Some(())
+ }
+
+ fn visit_curly_group(&mut self, group: &CurlyGroup) -> Option<()> {
+ for child in group.syntax().children_with_tokens() {
+ match child {
+ NodeOrToken::Node(node) => {
+ self.visit_value(&Value::cast(node)?)?;
+ }
+ NodeOrToken::Token(token) => {
+ match token.kind() {
+ L_CURLY | R_CURLY => (),
+ WHITESPACE | NBSP => self.data.text.push(' '),
+ _ => self.data.text.push_str(token.text()),
+ };
+ }
+ };
+ }
+
+ Some(())
+ }
+
+ fn visit_quote_group(&mut self, group: &QuoteGroup) -> Option<()> {
+ for child in group.syntax().children_with_tokens() {
+ match child {
+ NodeOrToken::Node(node) => {
+ self.visit_value(&Value::cast(node)?)?;
+ }
+ NodeOrToken::Token(token) => {
+ match token.kind() {
+ QUOTE => (),
+ WHITESPACE | NBSP => self.data.text.push(' '),
+ _ => self.data.text.push_str(token.text()),
+ };
+ }
+ };
+ }
+
+ Some(())
+ }
+
+ fn visit_join(&mut self, join: &Join) -> Option<()> {
+ if let Some(left) = join.left_value() {
+ self.visit_value(&left)?;
+ }
+
+ if let Some(right) = join.right_value() {
+ self.visit_value(&right)?;
+ }
+
+ Some(())
+ }
+
+ fn visit_accent(&mut self, accent: &Accent) -> Option<()> {
+ let name = accent.accent_name_token()?;
+ let word = accent.word_token()?;
+
+ let mut chars = word.text().chars();
+ let a = chars.next()?;
+
+ if chars.next().is_some() {
+ self.data.text.push_str(word.text());
+ } else {
+ let b = match name.text() {
+ r#"\`"# => '\u{0300}',
+ r#"\'"# => '\u{0301}',
+ r#"\^"# => '\u{0302}',
+ r#"\""# => '\u{0308}',
+ r#"\H"# => '\u{030B}',
+ r#"\~"# => '\u{0303}',
+
+ r#"\c"# => '\u{0327}',
+ r#"\k"# => '\u{0328}',
+ r#"\="# => '\u{0304}',
+ r#"\b"# => '\u{0331}',
+ r#"\."# => '\u{0307}',
+ r#"\d"# => '\u{0323}',
+ r#"\r"# => '\u{030A}',
+ r#"\u"# => '\u{0306}',
+ r#"\v"# => '\u{030C}',
+ _ => '\u{0000}',
+ };
+
+ match unicode_normalization::char::compose(a, b) {
+ Some(c) => self.data.text.push(c),
+ None => self.data.text.push_str(word.text()),
+ };
+ }
+
+ Some(())
+ }
+
+ fn visit_command(&mut self, command: &Command) -> Option<()> {
+ let name = command.command_name_token()?;
+ let replacement = match name.text() {
+ r#"\l"# => "\u{0142}",
+ r#"\o"# => "\u{00F8}",
+ r#"\i"# => "\u{0131}",
+ r#"\&"# => "&",
+ r#"\$"# => "$",
+ r#"\{"# => "{",
+ r#"\}"# => "}",
+ r#"\%"# => "%",
+ r#"\#"# => "#",
+ r#"\_"# => "_",
+ r#"\ "# | r#"\,"# | r#"\;"# => " ",
+ r#"\hyphen"# => "-",
+ r#"\TeX"# => "TeX",
+ r#"\LaTeX"# => "LaTeX",
+ text => text,
+ };
+
+ self.data.text.push_str(replacement);
+ Some(())
+ }
+}
diff --git a/support/texlab/src/citation/output.rs b/support/texlab/src/citation/output.rs
new file mode 100644
index 0000000000..555ec138db
--- /dev/null
+++ b/support/texlab/src/citation/output.rs
@@ -0,0 +1,87 @@
+use std::ops::Add;
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+pub enum Punct {
+ Nothing,
+ Space,
+ Comma,
+ Dot,
+ Colon,
+}
+
+impl Punct {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ Self::Nothing => "",
+ Self::Space => " ",
+ Self::Comma => ", ",
+ Self::Dot => ". ",
+ Self::Colon => ": ",
+ }
+ }
+}
+
+impl Add for Punct {
+ type Output = Punct;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ match (self, rhs) {
+ (Self::Nothing, Self::Nothing) => Self::Nothing,
+ (Self::Nothing, Self::Space)
+ | (Self::Space, Self::Nothing)
+ | (Self::Space, Self::Space) => Self::Space,
+ (Self::Nothing, Self::Comma)
+ | (Self::Space, Self::Comma)
+ | (Self::Comma, Self::Nothing)
+ | (Self::Comma, Self::Space)
+ | (Self::Comma, Self::Comma)
+ | (Self::Comma, Self::Dot)
+ | (Self::Dot, Self::Comma) => Self::Comma,
+ (Self::Nothing, Self::Dot)
+ | (Self::Space, Self::Dot)
+ | (Self::Dot, Self::Nothing)
+ | (Self::Dot, Self::Space)
+ | (Self::Dot, Self::Dot) => Self::Dot,
+ (Self::Nothing, Self::Colon)
+ | (Self::Space, Self::Colon)
+ | (Self::Comma, Self::Colon)
+ | (Self::Dot, Self::Colon)
+ | (Self::Colon, Self::Nothing)
+ | (Self::Colon, Self::Space)
+ | (Self::Colon, Self::Comma)
+ | (Self::Colon, Self::Dot)
+ | (Self::Colon, Self::Colon) => Self::Colon,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
+pub enum Inline {
+ Regular(String),
+ Italic(String),
+ Quoted(String),
+ Link { url: String, alt: String },
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Default)]
+pub struct InlineBuilder {
+ items: Vec<(Inline, Punct)>,
+}
+
+impl InlineBuilder {
+ pub fn push(&mut self, inline: Inline, leading: Punct, trailing: Punct) {
+ if let Some((_, last)) = self.items.last_mut() {
+ *last = *last + leading;
+ }
+
+ self.items.push((inline, trailing));
+ }
+
+ pub fn finish(mut self) -> impl Iterator<Item = (Inline, Punct)> {
+ if let Some((_, last)) = self.items.last_mut() {
+ *last = Punct::Nothing;
+ }
+
+ self.items.into_iter()
+ }
+}
diff --git a/support/texlab/src/citation/ris.rs b/support/texlab/src/citation/ris.rs
deleted file mode 100644
index 041fb654a0..0000000000
--- a/support/texlab/src/citation/ris.rs
+++ /dev/null
@@ -1,471 +0,0 @@
-// Ported from: https://github.com/michel-kraemer/citeproc-java/tree/master/citeproc-java/templates
-// Michel Kraemer
-// Apache License 2.0
-use citeproc_io::{unicode::is_latin_cyrillic, Date, DateOrRange, Name, NumberLike, Reference};
-use csl::*;
-use fnv::FnvHashMap;
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
-#[serde(rename_all = "UPPERCASE")]
-pub enum RisType {
- Abst,
- Advs,
- Aggr,
- Ancient,
- Art,
- Bill,
- Blog,
- Book,
- Case,
- Chap,
- Chart,
- Clswk,
- Comp,
- Conf,
- Cpaper,
- Ctlg,
- Data,
- Dbase,
- Dict,
- Ebook,
- Echap,
- Edbook,
- Ejour,
- Elec,
- Encyc,
- Equa,
- Figure,
- Gen,
- Govdoc,
- Grant,
- Hear,
- Icomm,
- Inpr,
- Jfull,
- Jour,
- Legal,
- Manscpt,
- Map,
- Mgzn,
- Mpct,
- Multi,
- Music,
- News,
- Pamp,
- Pat,
- Pcomm,
- Rprt,
- Ser,
- Slide,
- Sound,
- Stand,
- Stat,
- Std,
- Thes,
- Unpb,
- Video,
-}
-
-impl RisType {
- pub fn parse(ty: &str) -> Option<Self> {
- let ty = format!("\"{}\"", ty);
- serde_json::from_str(&ty).ok()
- }
-
- pub fn csl(self) -> CslType {
- match self {
- Self::Abst => CslType::Article,
- Self::Advs => CslType::Article,
- Self::Aggr => CslType::Dataset,
- Self::Ancient => CslType::Article,
- Self::Art => CslType::Article,
- Self::Bill => CslType::Bill,
- Self::Blog => CslType::Webpage,
- Self::Book => CslType::Book,
- Self::Case => CslType::LegalCase,
- Self::Chap => CslType::Chapter,
- Self::Chart => CslType::Article,
- Self::Clswk => CslType::Article,
- Self::Comp => CslType::Article,
- Self::Conf => CslType::PaperConference,
- Self::Cpaper => CslType::PaperConference,
- Self::Ctlg => CslType::Book,
- Self::Data => CslType::Dataset,
- Self::Dbase => CslType::Dataset,
- Self::Dict => CslType::EntryDictionary,
- Self::Ebook => CslType::Book,
- Self::Echap => CslType::Chapter,
- Self::Edbook => CslType::Book,
- Self::Ejour => CslType::ArticleJournal,
- Self::Elec => CslType::Article,
- Self::Encyc => CslType::EntryEncyclopedia,
- Self::Equa => CslType::Article,
- Self::Figure => CslType::Figure,
- Self::Gen => CslType::Article,
- Self::Govdoc => CslType::Legislation,
- Self::Grant => CslType::Legislation,
- Self::Hear => CslType::Article,
- Self::Icomm => CslType::PersonalCommunication,
- Self::Inpr => CslType::PaperConference,
- Self::Jfull => CslType::ArticleJournal,
- Self::Jour => CslType::ArticleJournal,
- Self::Legal => CslType::Legislation,
- Self::Manscpt => CslType::Manuscript,
- Self::Map => CslType::Map,
- Self::Mgzn => CslType::ArticleMagazine,
- Self::Mpct => CslType::MotionPicture,
- Self::Multi => CslType::Webpage,
- Self::Music => CslType::Song,
- Self::News => CslType::ArticleNewspaper,
- Self::Pamp => CslType::Pamphlet,
- Self::Pat => CslType::Patent,
- Self::Pcomm => CslType::PersonalCommunication,
- Self::Rprt => CslType::Report,
- Self::Ser => CslType::Article,
- Self::Slide => CslType::Article,
- Self::Sound => CslType::Song,
- Self::Stand => CslType::Article,
- Self::Stat => CslType::Legislation,
- Self::Std => CslType::Article,
- Self::Thes => CslType::Thesis,
- Self::Unpb => CslType::Article,
- Self::Video => CslType::MotionPicture,
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
-pub struct RisReference {
- pub id: Option<String>,
- pub ty: Option<RisType>,
- pub editors: Vec<String>,
- pub tertiary_authors: Vec<String>,
- pub subsidiary_authors: Vec<String>,
- pub abstrct: Option<String>,
- pub author_address: Option<String>,
- pub accession_number: Option<String>,
- pub authors: Vec<String>,
- pub book_or_conference: Option<String>,
- pub custom1: Option<String>,
- pub custom2: Option<String>,
- pub custom3: Option<String>,
- pub custom4: Option<String>,
- pub custom5: Option<String>,
- pub custom6: Option<String>,
- pub custom7: Option<String>,
- pub custom8: Option<String>,
- pub caption: Option<String>,
- pub call_number: Option<String>,
- pub place: Option<String>,
- pub date: Option<String>,
- pub name_of_database: Option<String>,
- pub doi: Option<String>,
- pub database_provider: Option<String>,
- pub end_page: Option<String>,
- pub edition: Option<String>,
- pub issue: Option<String>,
- pub journal: Option<String>,
- pub keywords: Vec<String>,
- pub file_attachments: Vec<String>,
- pub figure: Option<String>,
- pub language: Option<String>,
- pub label: Option<String>,
- pub number: Option<String>,
- pub type_of_work: Option<String>,
- pub notes: Vec<String>,
- pub number_of_volumes: Option<String>,
- pub original_publication: Option<String>,
- pub publisher: Option<String>,
- pub year: Option<String>,
- pub reviewed_item: Option<String>,
- pub research_notes: Option<String>,
- pub reprint_edition: Option<String>,
- pub section: Option<String>,
- pub isbn_or_issn: Option<String>,
- pub start_page: Option<String>,
- pub short_title: Option<String>,
- pub primary_title: Option<String>,
- pub secondardy_title: Option<String>,
- pub tertiary_title: Option<String>,
- pub translated_authors: Vec<String>,
- pub title: Option<String>,
- pub translated_title: Option<String>,
- pub url: Option<String>,
- pub volume: Option<String>,
- pub access_date: Option<String>,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
-pub struct RisLibrary {
- pub references: Vec<RisReference>,
-}
-
-impl RisLibrary {
- pub fn parse<'a, I: Iterator<Item = &'a str>>(lines: I) -> Self {
- let mut library = Self {
- references: Vec::new(),
- };
-
- let mut reference: RisReference = RisReference::default();
- for line in lines {
- let line = line.trim();
- if line == "" {
- continue;
- }
-
- if line == "ER -" {
- library.references.push(reference);
- reference = RisReference::default();
- continue;
- }
-
- let chars: Vec<_> = line.chars().collect();
- if chars.len() < 7 || chars[4] != '-' {
- continue;
- }
-
- let key: String = (&chars[..2]).iter().collect();
- let value: String = (&chars[6..]).iter().collect();
- match key.to_uppercase().as_str() {
- "TY" => reference.ty = RisType::parse(&value),
- "A2" => reference.editors.push(value),
- "A3" => reference.tertiary_authors.push(value),
- "A4" => reference.subsidiary_authors.push(value),
- "AB" => reference.abstrct = Some(value),
- "AD" => reference.author_address = Some(value),
- "AN" => reference.accession_number = Some(value),
- "AU" => reference.authors.push(value),
- "BT" => reference.book_or_conference = Some(value),
- "C1" => reference.custom1 = Some(value),
- "C2" => reference.custom2 = Some(value),
- "C3" => reference.custom3 = Some(value),
- "C4" => reference.custom4 = Some(value),
- "C5" => reference.custom5 = Some(value),
- "C6" => reference.custom6 = Some(value),
- "C7" => reference.custom7 = Some(value),
- "C8" => reference.custom8 = Some(value),
- "CA" => reference.caption = Some(value),
- "CN" => reference.call_number = Some(value),
- "CY" => reference.place = Some(value),
- "DA" => reference.date = Some(value),
- "DB" => reference.name_of_database = Some(value),
- "DO" => reference.doi = Some(value),
- "DP" => reference.database_provider = Some(value),
- "ED" => reference.editors.push(value),
- "EP" => reference.end_page = Some(value),
- "ET" => reference.edition = Some(value),
- "ID" => reference.id = Some(value),
- "IS" => reference.issue = Some(value),
- "JO" => reference.journal = Some(value),
- "J2" => reference.journal = Some(value),
- "KW" => reference.keywords.push(value),
- "L1" => reference.file_attachments.push(value),
- "L4" => reference.figure = Some(value),
- "LA" => reference.language = Some(value),
- "LB" => reference.label = Some(value),
- "M1" => reference.number = Some(value),
- "M3" => reference.type_of_work = Some(value),
- "N1" => reference.notes.push(value),
- "N2" => reference.abstrct = Some(value),
- "NV" => reference.number_of_volumes = Some(value),
- "OP" => reference.original_publication = Some(value),
- "PB" => reference.publisher = Some(value),
- "PY" => reference.year = Some(value),
- "RI" => reference.reviewed_item = Some(value),
- "RN" => reference.research_notes = Some(value),
- "RP" => reference.reprint_edition = Some(value),
- "SE" => reference.section = Some(value),
- "SN" => reference.isbn_or_issn = Some(value),
- "SP" => reference.start_page = Some(value),
- "ST" => reference.short_title = Some(value),
- "T1" => reference.primary_title = Some(value),
- "T2" => reference.secondardy_title = Some(value),
- "T3" => reference.tertiary_title = Some(value),
- "TA" => reference.translated_authors.push(value),
- "TI" => reference.title = Some(value),
- "TT" => reference.translated_title = Some(value),
- "U1" => reference.type_of_work = Some(value),
- "UR" => reference.url = Some(value),
- "VL" => reference.volume = Some(value),
- "Y2" => reference.access_date = Some(value),
- _ => (),
- }
- }
- library
- }
-}
-
-impl Into<Reference> for RisReference {
- fn into(self) -> Reference {
- let csl_type = self.ty.expect("RIS type is missing").csl();
- let mut date: FnvHashMap<DateVariable, DateOrRange> = FnvHashMap::default();
- let mut name: FnvHashMap<NameVariable, Vec<Name>> = FnvHashMap::default();
- let mut number: FnvHashMap<NumberVariable, NumberLike> = FnvHashMap::default();
- let mut ordinary: FnvHashMap<Variable, String> = FnvHashMap::default();
-
- if let Some(access_date) = self.access_date {
- date.insert(DateVariable::Accessed, parse_date_or_range(access_date));
- }
-
- name.insert(NameVariable::Author, parse_authors(self.authors));
- name.insert(NameVariable::Editor, parse_authors(self.editors));
-
- if let Some(container_title) = self
- .journal
- .or(self.name_of_database)
- .or(self.book_or_conference)
- {
- ordinary.insert(Variable::ContainerTitle, container_title.clone());
- ordinary.insert(Variable::CollectionTitle, container_title);
- }
-
- if let Some(value) = self.date.or(self.year) {
- let value = parse_date_or_range(value);
- date.insert(DateVariable::Issued, value.clone());
- date.insert(DateVariable::EventDate, value);
- }
-
- if let Some(url) = self.url {
- ordinary.insert(Variable::URL, url);
- }
-
- let notes = self.notes;
- ordinary.insert(
- Variable::Note,
- self.research_notes.unwrap_or_else(|| notes.join("\n")),
- );
-
- if let Some(issue) = self.issue {
- number.insert(NumberVariable::Issue, parse_number(issue));
- }
-
- if let Some(num) = self.number {
- number.insert(NumberVariable::Number, parse_number(num));
- }
-
- if let Some(place) = self.place {
- ordinary.insert(Variable::EventPlace, place.clone());
- ordinary.insert(Variable::PublisherPlace, place);
- }
-
- if let Some(abstrct) = self.abstrct {
- ordinary.insert(Variable::Abstract, abstrct);
- }
-
- if let Some(call_number) = self.call_number {
- ordinary.insert(Variable::CallNumber, call_number);
- }
-
- if let Some(doi) = self.doi {
- ordinary.insert(Variable::DOI, doi);
- }
-
- if let Some(edition) = self.edition {
- number.insert(NumberVariable::Edition, parse_number(edition));
- }
-
- if let Some(isbn_or_issn) = self.isbn_or_issn {
- ordinary.insert(Variable::ISBN, isbn_or_issn.clone());
- ordinary.insert(Variable::ISSN, isbn_or_issn);
- }
-
- ordinary.insert(Variable::Keyword, self.keywords.join(", "));
-
- if let Some(number_of_volumes) = self.number_of_volumes {
- number.insert(
- NumberVariable::NumberOfVolumes,
- parse_number(number_of_volumes),
- );
- }
-
- if let Some(original_publication) = self.original_publication {
- ordinary.insert(Variable::OriginalTitle, original_publication);
- }
-
- match (self.start_page, self.end_page) {
- (Some(start_page), Some(end_page)) => {
- number.insert(
- NumberVariable::Page,
- NumberLike::Str(format!("{}-{}", start_page, end_page).into()),
- );
- }
- (Some(page), None) | (None, Some(page)) => {
- number.insert(NumberVariable::Page, parse_number(page));
- }
- (None, None) => {}
- }
-
- if let Some(publisher) = self.publisher {
- ordinary.insert(Variable::Publisher, publisher);
- }
-
- if let Some(reviewed_item) = self.reviewed_item {
- ordinary.insert(Variable::ReviewedTitle, reviewed_item);
- }
-
- if let Some(section) = self.section {
- ordinary.insert(Variable::Section, section);
- }
-
- if let Some(short_title) = self.short_title {
- ordinary.insert(Variable::TitleShort, short_title);
- }
-
- if let Some(title) = self.title {
- ordinary.insert(Variable::Title, title);
- }
-
- if let Some(volume) = self.volume {
- number.insert(NumberVariable::Volume, parse_number(volume));
- }
-
- Reference {
- id: Atom::from(self.id.or(self.label).expect("RIS id is missing").as_str()),
- csl_type,
- language: None,
- name,
- number,
- date,
- ordinary,
- }
- }
-}
-
-fn parse_number(value: String) -> NumberLike {
- match value.parse() {
- Ok(value) => NumberLike::Num(value),
- Err(_) => NumberLike::Str(value.into()),
- }
-}
-
-fn parse_authors(authors: Vec<String>) -> Vec<Name> {
- authors
- .into_iter()
- .map(|author| Name::Literal {
- is_latin_cyrillic: is_latin_cyrillic(&author),
- literal: author.into(),
- })
- .collect()
-}
-
-fn parse_date_or_range(value: String) -> DateOrRange {
- parse_date(&value)
- .map(DateOrRange::Single)
- .unwrap_or_else(|| DateOrRange::Literal {
- literal: value.into(),
- circa: false,
- })
-}
-
-fn parse_date(value: &str) -> Option<Date> {
- let mut parts = value.split('/');
- let year: i32 = parts.next()?.parse().ok()?;
- match parts.next().and_then(|p| p.parse().ok()) {
- Some(month) => match parts.next().and_then(|p| p.parse().ok()) {
- Some(day) => Some(Date::new(year, month, day)),
- None => Some(Date::new(year, month, 0)),
- },
- None => Some(Date::new(year, 0, 0)),
- }
-}
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap
new file mode 100644
index 0000000000..4598d47ed9
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 64
+expression: "render_entry(r#\"\n@string{jomch = {J.~Organomet. Chem.}}\n\n@article{aksin,\n author = {Aks{\\i}n, {\\\"O}zge and T{\\\"u}rkmen, Hayati and Artok, Levent\n and {\\c{C}}etinkaya, Bekir and Ni, Chaoying and\n B{\\\"u}y{\\\"u}kg{\\\"u}ng{\\\"o}r, Orhan and {\\\"O}zkal, Erhan},\n title = {Effect of immobilization on catalytic characteristics of\n saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}\n reactions},\n journaltitle = jomch,\n date = 2006,\n volume = 691,\n number = 13,\n pages = {3027-3036},\n indextitle = {Effect of immobilization on catalytic characteristics},\n}\"#)"
+---
+O. Aksın, H. Türkmen, L. Artok, B. Çetinkaya, C. Ni, O. Büyükgüngör, E. Özkal: "Effect of immobilization on catalytic characteristics of saturated Pd-N-heterocyclic carbenes in Mizoroki-Heck reactions". *J. Organomet. Chem.* 691.13 (2006): 3027-3036.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap
new file mode 100644
index 0000000000..dd857419a5
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 87
+expression: "render_entry(r#\"\n@string{jams = {J.~Amer. Math. Soc.}}\n\n@article{bertram,\n author = {Bertram, Aaron and Wentworth, Richard},\n title = {Gromov invariants for holomorphic maps on {Riemann} surfaces},\n journaltitle = jams,\n date = 1996,\n volume = 9,\n number = 2,\n pages = {529-571},\n langid = {english},\n langidopts = {variant=american},\n shorttitle = {Gromov invariants},\n annotation = {An \\texttt{article} entry with a \\texttt{volume} and a\n \\texttt{number} field},\n}\"#)"
+---
+A. Bertram, R. Wentworth: "Gromov invariants for holomorphic maps on Riemann surfaces". *J. Amer. Math. Soc.* 9.2 (1996): 529-571.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap
new file mode 100644
index 0000000000..851b874fff
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 138
+expression: "render_entry(r#\"\n@article{DBLP:journals/corr/abs-2107-11903,\n author = {Michelle L. Blom and\n Jurlind Budurushi and\n Ronald L. Rivest and\n Philip B. Stark and\n Peter J. Stuckey and\n Vanessa Teague and\n Damjan Vukcevic},\n title = {Assertion-based Approaches to Auditing Complex Elections, with application\n to party-list proportional elections},\n journal = {CoRR},\n volume = {abs/2107.11903},\n year = {2021},\n url = {https://arxiv.org/abs/2107.11903},\n eprinttype = {arXiv},\n eprint = {2107.11903},\n timestamp = {Thu, 29 Jul 2021 16:14:15 +0200},\n biburl = {https://dblp.org/rec/journals/corr/abs-2107-11903.bib},\n bibsource = {dblp computer science bibliography, https://dblp.org}\n}\"#)"
+---
+M. Blom, J. Budurushi, R. Rivest, P. Stark, P. Stuckey, V. Teague, D. Vukcevic: "Assertion-based Approaches to Auditing Complex Elections, with application to party-list proportional elections". *CoRR* abs/2107.11903 (2021): arXiv: [2107.11903](https://arxiv.org/abs/2107.11903). URL: [https://arxiv.org/abs/2107.11903](https://arxiv.org/abs/2107.11903).
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap
new file mode 100644
index 0000000000..2eefc3a871
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 39
+expression: "render_entry(r#\"\n@article{10.1145/331499.331504,\n author = {Jain, A. K. and Murty, M. N. and Flynn, P. J.},\n title = {Data Clustering: A Review},\n year = {1999},\n issue_date = {Sept. 1999},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n volume = {31},\n number = {3},\n issn = {0360-0300},\n url = {https://doi.org/10.1145/331499.331504},\n doi = {10.1145/331499.331504},\n journal = {ACM Comput. Surv.},\n month = {sep},\n pages = {264-323},\n numpages = {60},\n keywords = {incremental clustering, clustering applications, exploratory data analysis, cluster analysis, similarity indices, unsupervised learning}\n}\"#)"
+---
+A. Jain, M. Murty, P. Flynn: "Data Clustering: A Review". *ACM Comput. Surv.* 31.3 (Sep. 1999): 264-323. ISSN: 0360-0300. DOI: [10.1145/331499.331504](https://doi.org/10.1145/331499.331504). URL: [https://doi.org/10.1145/331499.331504](https://doi.org/10.1145/331499.331504).
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap
new file mode 100644
index 0000000000..d1cae5db36
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 110
+expression: "render_entry(r#\"\n@string{jchph = {J.~Chem. Phys.}}\n\n@article{kastenholz,\n author = {Kastenholz, M. A. and H{\\\"u}nenberger, Philippe H.},\n title = {Computation of methodology\\hyphen independent ionic solvation\n free energies from molecular simulations},\n journaltitle = jchph,\n date = 2006,\n subtitle = {{I}. {The} electrostatic potential in molecular liquids},\n volume = 124,\n eid = 124106,\n doi = {10.1063/1.2172593},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Computation of ionic solvation free energies},\n annotation = {An \\texttt{article} entry with an \\texttt{eid} and a\n \\texttt{doi} field. Note that the \\textsc{doi} is transformed\n into a clickable link if \\texttt{hyperref} support has been\n enabled},\n}\n \"#)"
+---
+M. Kastenholz, P. Hünenberger: "Computation of methodology- independent ionic solvation free energies from molecular simulations. I. The electrostatic potential in molecular liquids". *J. Chem. Phys.* 124, 124106 (2006): DOI: [10.1063/1.2172593](https://doi.org/10.1063/1.2172593).
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap
new file mode 100644
index 0000000000..3dfcb2967c
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 15
+expression: "render_entry(r#\"\n@article{10.1145/359340.359342,\n author = {Rivest, R. L. and Shamir, A. and Adleman, L.},\n title = {A Method for Obtaining Digital Signatures and Public-Key Cryptosystems},\n year = {1978},\n issue_date = {Feb. 1978},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n volume = {21},\n number = {2},\n issn = {0001-0782},\n url = {https://doi.org/10.1145/359340.359342},\n doi = {10.1145/359340.359342},\n journal = {Commun. ACM},\n month = {feb},\n pages = {120-126},\n numpages = {7},\n}\"#)"
+---
+R. Rivest, A. Shamir, L. Adleman: "A Method for Obtaining Digital Signatures and Public-Key Cryptosystems". *Commun. ACM* 21.2 (Feb. 1978): 120-126. ISSN: 0001-0782. DOI: [10.1145/359340.359342](https://doi.org/10.1145/359340.359342). URL: [https://doi.org/10.1145/359340.359342](https://doi.org/10.1145/359340.359342).
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap
new file mode 100644
index 0000000000..34c0aeda3b
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 165
+expression: "render_entry(r#\"\n@book{10.5555/1177220,\n author = {Aho, Alfred V. and Lam, Monica S. and Sethi, Ravi and Ullman, Jeffrey D.},\n title = {Compilers: Principles, Techniques, and Tools (2nd Edition)},\n year = {2006},\n isbn = {0321486811},\n publisher = {Addison-Wesley Longman Publishing Co., Inc.},\n address = {USA}\n}\"#)"
+---
+A. Aho, M. Lam, R. Sethi, J. Ullman: "Compilers: Principles, Techniques, and Tools (2nd Edition)". Addison-Wesley Longman Publishing Co., Inc., 2006. ISBN: 0321486811.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap
new file mode 100644
index 0000000000..32b8a6ca5f
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 180
+expression: "render_entry(r#\"\n@book{averroes/bland,\n author = {Averroes},\n title = {The Epistle on the Possibility of Conjunction with the Active\n Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}},\n date = 1982,\n editor = {Bland, Kalman P.},\n translator = {Bland, Kalman P.},\n series = {Moreshet: Studies in {Jewish} History, Literature and Thought},\n number = 7,\n publisher = {Jewish Theological Seminary of America},\n location = {New York},\n keywords = {primary},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Epistle on the Possibility of Conjunction, The},\n shorttitle = {Possibility of Conjunction},\n annotation = {A \\texttt{book} entry with a \\texttt{series} and a\n \\texttt{number}. Note the concatenation of the \\texttt{editor}\n and \\texttt{translator} fields as well as the\n \\texttt{indextitle} field},\n}\"#)"
+---
+"The Epistle on the Possibility of Conjunction with the Active Intellect by Ibn Rushd with the Commentary of Moses Narboni". Ed. by K. Bland. Trans. by K. Bland. Moreshet: Studies in Jewish History, Literature and Thought 7. New York: Jewish Theological Seminary of America, 1982.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap
new file mode 100644
index 0000000000..0276be0589
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 208
+expression: "render_entry(r#\"\n@book{knuth:ct:a,\n author = {Knuth, Donald E.},\n title = {The {\\TeX book}},\n date = 1984,\n maintitle = {Computers \\& Typesetting},\n volume = {A},\n publisher = {Addison-Wesley},\n location = {Reading, Mass.},\n langid = {english},\n langidopts = {variant=american},\n sorttitle = {Computers & Typesetting A},\n indexsorttitle= {The TeXbook},\n indextitle = {\\protect\\TeX book, The},\n shorttitle = {\\TeX book},\n annotation = {The first volume of a five-volume book. Note the\n \\texttt{sorttitle} field. We want this\n volume to be listed after the entry referring to the entire\n five-volume set. Also note the \\texttt{indextitle} and\n \\texttt{indexsorttitle} fields. Indexing packages that don't\n generate robust index entries require some control sequences\n to be protected from expansion},\n}\n \"#)"
+---
+D. Knuth: "The TeX book". *Computers & Typesetting*. Vol. A. Reading, Mass.: Addison-Wesley, 1984.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap
new file mode 100644
index 0000000000..9ea78c80cf
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 313
+expression: "render_entry(r#\"\n@collection{matuz:doody,\n editor = {Matuz, Roger},\n title = {Contemporary Literary Criticism},\n year = 1990,\n volume = 61,\n publisher = {Gale},\n location = {Detroit},\n pages = {204-208},\n langid = {english},\n langidopts = {variant=american},\n annotation = {A \\texttt{collection} entry providing the excerpt information\n for the \\texttt{doody} entry. Note the format of the\n \\texttt{pages} field},\n}\"#)"
+---
+"Contemporary Literary Criticism". Ed. by R. Matuz. Vol. 61. Detroit: Gale, 1990, 204-208.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap
new file mode 100644
index 0000000000..9baef38be7
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 290
+expression: "render_entry(r#\"\n@inproceedings{10.1145/967900.968040,\n author = {Combi, Carlo and Pozzi, Giuseppe},\n title = {Architectures for a Temporal Workflow Management System},\n year = {2004},\n isbn = {1581138121},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n url = {https://doi.org/10.1145/967900.968040},\n doi = {10.1145/967900.968040},\n booktitle = {Proceedings of the 2004 ACM Symposium on Applied Computing},\n pages = {659-666},\n numpages = {8},\n keywords = {active DBMS, temporal DBMS, workflow management system - WfMS, temporal workflow management system},\n location = {Nicosia, Cyprus},\n series = {SAC '04}\n}\"#)"
+---
+C. Combi, G. Pozzi: "Architectures for a Temporal Workflow Management System". *Proceedings of the 2004 ACM Symposium on Applied Computing*. SAC '04. Nicosia, Cyprus: Association for Computing Machinery, 2004, 659-666. ISBN: 1581138121. DOI: [10.1145/967900.968040](https://doi.org/10.1145/967900.968040). URL: [https://doi.org/10.1145/967900.968040](https://doi.org/10.1145/967900.968040).
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap
new file mode 100644
index 0000000000..df9ab6569f
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 269
+expression: "render_entry(r#\"\n@inproceedings{10.5555/1386993.1386994,\n author = {Erwin, Alva and Gopalan, Raj P. and Achuthan, N. R.},\n title = {A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets},\n year = {2007},\n isbn = {9781920682651},\n publisher = {Australian Computer Society, Inc.},\n address = {AUS},\n booktitle = {Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84},\n pages = {3-11},\n numpages = {9},\n keywords = {pattern growth, high utility itemset mining},\n location = {Gold Coast, Australia},\n series = {AIDM '07}\n}\"#)"
+---
+A. Erwin, R. Gopalan, N. Achuthan: "A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets". *Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84*. AIDM '07. Gold Coast, Australia: Australian Computer Society, Inc., 2007, 3-11. ISBN: 9781920682651.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap
new file mode 100644
index 0000000000..5162b3114b
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 238
+expression: "render_entry(r#\"\n@string{dtv = {Deutscher Taschenbuch-Verlag}}\n\n@mvbook{nietzsche:ksa,\n author = {Nietzsche, Friedrich},\n title = {S{\\\"a}mtliche Werke},\n date = 1988,\n editor = {Colli, Giorgio and Montinari, Mazzino},\n edition = 2,\n volumes = 15,\n publisher = dtv # { and Walter de Gruyter},\n location = {M{\\\"u}nchen and Berlin and New York},\n langid = {german},\n sorttitle = {Werke-00-000},\n indexsorttitle= {Samtliche Werke},\n subtitle = {Kritische Studienausgabe},\n annotation = {The critical edition of Nietzsche's works. This is a\n \\texttt{mvbook} entry referring to a 15-volume work as a\n whole. Note the \\texttt{volumes} field and the format of the\n \\texttt{publisher} and \\texttt{location} fields in the\n database file. Also note the \\texttt{sorttitle} and\n field which is used to fine-tune the\n sorting order of the bibliography. We want this item listed\n first in the bibliography},\n}\"#)"
+---
+F. Nietzsche: "Sämtliche Werke. Kritische Studienausgabe". Ed. by G. Colli, M. Montinari. 2nd. München and Berlin and New York: Deutscher Taschenbuch-Verlag and Walter de Gruyter, 1988.
diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap
new file mode 100644
index 0000000000..6f8c17f059
--- /dev/null
+++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation/tests.rs
+assertion_line: 334
+expression: "render_entry(r#\"\n@patent{almendro,\n author = {Almendro, Jos{\\'e} L. and Mart{\\'i}n, Jacinto and S{\\'a}nchez,\n Alberto and Nozal, Fernando},\n title = {Elektromagnetisches Signalhorn},\n number = {EU-29702195U},\n date = 1998,\n location = {countryfr and countryuk and countryde},\n langid = {german},\n annotation = {This is a \\texttt{patent} entry with a \\texttt{location}\n field. The number is given in the \\texttt{number} field. Note\n the format of the \\texttt{location} field in the database\n file. Compare \\texttt{laufenberg}, \\texttt{sorace}, and\n \\texttt{kowalik}},\n}\"#)"
+---
+J. Almendro, J. Martín, A. Sánchez, F. Nozal: "Elektromagnetisches Signalhorn". EU-29702195U (France and United Kingdom and Germany). 1998.
diff --git a/support/texlab/src/citation/tests.rs b/support/texlab/src/citation/tests.rs
new file mode 100644
index 0000000000..16aefea11d
--- /dev/null
+++ b/support/texlab/src/citation/tests.rs
@@ -0,0 +1,351 @@
+use insta::assert_snapshot;
+use rowan::ast::AstNode;
+
+use crate::syntax::bibtex;
+
+fn render_entry(input: &str) -> String {
+ let green = bibtex::parse(input);
+ let root = bibtex::Root::cast(bibtex::SyntaxNode::new_root(green)).unwrap();
+ let entry = root.entries().next().unwrap();
+ super::render(&entry).unwrap()
+}
+
+#[test]
+fn article_rivest_1978() {
+ assert_snapshot!(render_entry(
+ r#"
+@article{10.1145/359340.359342,
+ author = {Rivest, R. L. and Shamir, A. and Adleman, L.},
+ title = {A Method for Obtaining Digital Signatures and Public-Key Cryptosystems},
+ year = {1978},
+ issue_date = {Feb. 1978},
+ publisher = {Association for Computing Machinery},
+ address = {New York, NY, USA},
+ volume = {21},
+ number = {2},
+ issn = {0001-0782},
+ url = {https://doi.org/10.1145/359340.359342},
+ doi = {10.1145/359340.359342},
+ journal = {Commun. ACM},
+ month = {feb},
+ pages = {120-126},
+ numpages = {7},
+}"#
+ ));
+}
+
+#[test]
+fn article_jain_1999() {
+ assert_snapshot!(render_entry(
+ r#"
+@article{10.1145/331499.331504,
+ author = {Jain, A. K. and Murty, M. N. and Flynn, P. J.},
+ title = {Data Clustering: A Review},
+ year = {1999},
+ issue_date = {Sept. 1999},
+ publisher = {Association for Computing Machinery},
+ address = {New York, NY, USA},
+ volume = {31},
+ number = {3},
+ issn = {0360-0300},
+ url = {https://doi.org/10.1145/331499.331504},
+ doi = {10.1145/331499.331504},
+ journal = {ACM Comput. Surv.},
+ month = {sep},
+ pages = {264-323},
+ numpages = {60},
+ keywords = {incremental clustering, clustering applications, exploratory data analysis, cluster analysis, similarity indices, unsupervised learning}
+}"#
+ ));
+}
+
+#[test]
+fn article_aksin_2006() {
+ assert_snapshot!(render_entry(
+ r#"
+@string{jomch = {J.~Organomet. Chem.}}
+
+@article{aksin,
+ author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent
+ and {\c{C}}etinkaya, Bekir and Ni, Chaoying and
+ B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan},
+ title = {Effect of immobilization on catalytic characteristics of
+ saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}
+ reactions},
+ journaltitle = jomch,
+ date = 2006,
+ volume = 691,
+ number = 13,
+ pages = {3027-3036},
+ indextitle = {Effect of immobilization on catalytic characteristics},
+}"#
+ ));
+}
+
+#[test]
+fn article_betram_1996() {
+ assert_snapshot!(render_entry(
+ r#"
+@string{jams = {J.~Amer. Math. Soc.}}
+
+@article{bertram,
+ author = {Bertram, Aaron and Wentworth, Richard},
+ title = {Gromov invariants for holomorphic maps on {Riemann} surfaces},
+ journaltitle = jams,
+ date = 1996,
+ volume = 9,
+ number = 2,
+ pages = {529-571},
+ langid = {english},
+ langidopts = {variant=american},
+ shorttitle = {Gromov invariants},
+ annotation = {An \texttt{article} entry with a \texttt{volume} and a
+ \texttt{number} field},
+}"#
+ ));
+}
+
+#[test]
+fn article_kastenholz_2006() {
+ assert_snapshot!(render_entry(
+ r#"
+@string{jchph = {J.~Chem. Phys.}}
+
+@article{kastenholz,
+ author = {Kastenholz, M. A. and H{\"u}nenberger, Philippe H.},
+ title = {Computation of methodology\hyphen independent ionic solvation
+ free energies from molecular simulations},
+ journaltitle = jchph,
+ date = 2006,
+ subtitle = {{I}. {The} electrostatic potential in molecular liquids},
+ volume = 124,
+ eid = 124106,
+ doi = {10.1063/1.2172593},
+ langid = {english},
+ langidopts = {variant=american},
+ indextitle = {Computation of ionic solvation free energies},
+ annotation = {An \texttt{article} entry with an \texttt{eid} and a
+ \texttt{doi} field. Note that the \textsc{doi} is transformed
+ into a clickable link if \texttt{hyperref} support has been
+ enabled},
+}
+ "#
+ ));
+}
+
+#[test]
+fn article_blom_2021() {
+ assert_snapshot!(render_entry(
+ r#"
+@article{DBLP:journals/corr/abs-2107-11903,
+ author = {Michelle L. Blom and
+ Jurlind Budurushi and
+ Ronald L. Rivest and
+ Philip B. Stark and
+ Peter J. Stuckey and
+ Vanessa Teague and
+ Damjan Vukcevic},
+ title = {Assertion-based Approaches to Auditing Complex Elections, with application
+ to party-list proportional elections},
+ journal = {CoRR},
+ volume = {abs/2107.11903},
+ year = {2021},
+ url = {https://arxiv.org/abs/2107.11903},
+ eprinttype = {arXiv},
+ eprint = {2107.11903},
+ timestamp = {Thu, 29 Jul 2021 16:14:15 +0200},
+ biburl = {https://dblp.org/rec/journals/corr/abs-2107-11903.bib},
+ bibsource = {dblp computer science bibliography, https://dblp.org}
+}"#
+ ));
+}
+
+#[test]
+fn book_aho_2006() {
+ assert_snapshot!(render_entry(
+ r#"
+@book{10.5555/1177220,
+ author = {Aho, Alfred V. and Lam, Monica S. and Sethi, Ravi and Ullman, Jeffrey D.},
+ title = {Compilers: Principles, Techniques, and Tools (2nd Edition)},
+ year = {2006},
+ isbn = {0321486811},
+ publisher = {Addison-Wesley Longman Publishing Co., Inc.},
+ address = {USA}
+}"#
+ ));
+}
+
+#[test]
+fn book_averroes_1998() {
+ assert_snapshot!(render_entry(
+ r#"
+@book{averroes/bland,
+ author = {Averroes},
+ title = {The Epistle on the Possibility of Conjunction with the Active
+ Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}},
+ date = 1982,
+ editor = {Bland, Kalman P.},
+ translator = {Bland, Kalman P.},
+ series = {Moreshet: Studies in {Jewish} History, Literature and Thought},
+ number = 7,
+ publisher = {Jewish Theological Seminary of America},
+ location = {New York},
+ keywords = {primary},
+ langid = {english},
+ langidopts = {variant=american},
+ indextitle = {Epistle on the Possibility of Conjunction, The},
+ shorttitle = {Possibility of Conjunction},
+ annotation = {A \texttt{book} entry with a \texttt{series} and a
+ \texttt{number}. Note the concatenation of the \texttt{editor}
+ and \texttt{translator} fields as well as the
+ \texttt{indextitle} field},
+}"#
+ ));
+}
+
+#[test]
+fn book_knuth_1984() {
+ assert_snapshot!(render_entry(
+ r#"
+@book{knuth:ct:a,
+ author = {Knuth, Donald E.},
+ title = {The {\TeX book}},
+ date = 1984,
+ maintitle = {Computers \& Typesetting},
+ volume = {A},
+ publisher = {Addison-Wesley},
+ location = {Reading, Mass.},
+ langid = {english},
+ langidopts = {variant=american},
+ sorttitle = {Computers & Typesetting A},
+ indexsorttitle= {The TeXbook},
+ indextitle = {\protect\TeX book, The},
+ shorttitle = {\TeX book},
+ annotation = {The first volume of a five-volume book. Note the
+ \texttt{sorttitle} field. We want this
+ volume to be listed after the entry referring to the entire
+ five-volume set. Also note the \texttt{indextitle} and
+ \texttt{indexsorttitle} fields. Indexing packages that don't
+ generate robust index entries require some control sequences
+ to be protected from expansion},
+}
+ "#
+ ));
+}
+
+#[test]
+fn mvbook_nietzsche_1988() {
+ assert_snapshot!(render_entry(
+ r#"
+@string{dtv = {Deutscher Taschenbuch-Verlag}}
+
+@mvbook{nietzsche:ksa,
+ author = {Nietzsche, Friedrich},
+ title = {S{\"a}mtliche Werke},
+ date = 1988,
+ editor = {Colli, Giorgio and Montinari, Mazzino},
+ edition = 2,
+ volumes = 15,
+ publisher = dtv # { and Walter de Gruyter},
+ location = {M{\"u}nchen and Berlin and New York},
+ langid = {german},
+ sorttitle = {Werke-00-000},
+ indexsorttitle= {Samtliche Werke},
+ subtitle = {Kritische Studienausgabe},
+ annotation = {The critical edition of Nietzsche's works. This is a
+ \texttt{mvbook} entry referring to a 15-volume work as a
+ whole. Note the \texttt{volumes} field and the format of the
+ \texttt{publisher} and \texttt{location} fields in the
+ database file. Also note the \texttt{sorttitle} and
+ field which is used to fine-tune the
+ sorting order of the bibliography. We want this item listed
+ first in the bibliography},
+}"#
+ ));
+}
+
+#[test]
+fn inproceedings_erwin_2007() {
+ assert_snapshot!(render_entry(
+ r#"
+@inproceedings{10.5555/1386993.1386994,
+ author = {Erwin, Alva and Gopalan, Raj P. and Achuthan, N. R.},
+ title = {A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets},
+ year = {2007},
+ isbn = {9781920682651},
+ publisher = {Australian Computer Society, Inc.},
+ address = {AUS},
+ booktitle = {Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84},
+ pages = {3-11},
+ numpages = {9},
+ keywords = {pattern growth, high utility itemset mining},
+ location = {Gold Coast, Australia},
+ series = {AIDM '07}
+}"#
+ ));
+}
+
+#[test]
+fn inproceedings_combi_2004() {
+ assert_snapshot!(render_entry(
+ r#"
+@inproceedings{10.1145/967900.968040,
+ author = {Combi, Carlo and Pozzi, Giuseppe},
+ title = {Architectures for a Temporal Workflow Management System},
+ year = {2004},
+ isbn = {1581138121},
+ publisher = {Association for Computing Machinery},
+ address = {New York, NY, USA},
+ url = {https://doi.org/10.1145/967900.968040},
+ doi = {10.1145/967900.968040},
+ booktitle = {Proceedings of the 2004 ACM Symposium on Applied Computing},
+ pages = {659-666},
+ numpages = {8},
+ keywords = {active DBMS, temporal DBMS, workflow management system - WfMS, temporal workflow management system},
+ location = {Nicosia, Cyprus},
+ series = {SAC '04}
+}"#
+ ));
+}
+
+#[test]
+fn collection_matuz_1990() {
+ assert_snapshot!(render_entry(
+ r#"
+@collection{matuz:doody,
+ editor = {Matuz, Roger},
+ title = {Contemporary Literary Criticism},
+ year = 1990,
+ volume = 61,
+ publisher = {Gale},
+ location = {Detroit},
+ pages = {204-208},
+ langid = {english},
+ langidopts = {variant=american},
+ annotation = {A \texttt{collection} entry providing the excerpt information
+ for the \texttt{doody} entry. Note the format of the
+ \texttt{pages} field},
+}"#
+ ));
+}
+
+#[test]
+fn patent_almendro_1998() {
+ assert_snapshot!(render_entry(
+ r#"
+@patent{almendro,
+ author = {Almendro, Jos{\'e} L. and Mart{\'i}n, Jacinto and S{\'a}nchez,
+ Alberto and Nozal, Fernando},
+ title = {Elektromagnetisches Signalhorn},
+ number = {EU-29702195U},
+ date = 1998,
+ location = {countryfr and countryuk and countryde},
+ langid = {german},
+ annotation = {This is a \texttt{patent} entry with a \texttt{location}
+ field. The number is given in the \texttt{number} field. Note
+ the format of the \texttt{location} field in the database
+ file. Compare \texttt{laufenberg}, \texttt{sorace}, and
+ \texttt{kowalik}},
+}"#
+ ));
+}
diff --git a/support/texlab/src/component_db.rs b/support/texlab/src/component_db.rs
index 7676feff5c..68788df879 100644
--- a/support/texlab/src/component_db.rs
+++ b/support/texlab/src/component_db.rs
@@ -7,7 +7,7 @@ use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
-use crate::WorkspaceSubset;
+use crate::Workspace;
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -35,9 +35,9 @@ impl ComponentDatabase {
})
}
- pub fn linked_components<'a>(&self, subset: &WorkspaceSubset) -> Vec<&Component> {
+ pub fn linked_components(&self, workspace: &Workspace) -> Vec<&Component> {
let mut start_components = vec![self.kernel()];
- for document in &subset.documents {
+ for document in workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
data.extras
.explicit_links
@@ -54,7 +54,7 @@ impl ComponentDatabase {
component
.references
.iter()
- .flat_map(|file| self.find(&file))
+ .flat_map(|file| self.find(file))
.for_each(|component| all_components.push(component))
}
@@ -89,7 +89,7 @@ impl ComponentDatabase {
.iter()
.find(|metadata| metadata.name == name)?;
- let desc = metadata.description.to_owned()?;
+ let desc = metadata.description.clone()?;
Some(MarkupContent {
kind: MarkupKind::PlainText,
value: desc,
diff --git a/support/texlab/src/config.rs b/support/texlab/src/config.rs
deleted file mode 100644
index 5e9703ef3a..0000000000
--- a/support/texlab/src/config.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use std::sync::{Mutex, RwLock};
-
-use crossbeam_channel::Sender;
-use log::{error, warn};
-use lsp_server::Message;
-use lsp_types::{
- notification::{DidChangeConfiguration, Notification},
- request::{RegisterCapability, WorkspaceConfiguration},
- ClientCapabilities, ConfigurationItem, ConfigurationParams, Registration, RegistrationParams,
-};
-
-use crate::{client::send_request, req_queue::ReqQueue, ClientCapabilitiesExt, Options};
-
-pub fn register_config_capability(
- req_queue: &Mutex<ReqQueue>,
- sender: &Sender<Message>,
- client_capabilities: &Mutex<ClientCapabilities>,
-) {
- let client_capabilities = client_capabilities.lock().unwrap();
- if client_capabilities.has_push_configuration_support() {
- drop(client_capabilities);
- let reg = Registration {
- id: "pull-config".to_string(),
- method: DidChangeConfiguration::METHOD.to_string(),
- register_options: None,
- };
-
- let params = RegistrationParams {
- registrations: vec![reg],
- };
-
- if let Err(why) = send_request::<RegisterCapability>(&req_queue, &sender, params) {
- error!(
- "Failed to register \"{}\" notification: {}",
- DidChangeConfiguration::METHOD,
- why
- );
- }
- }
-}
-
-pub fn pull_config(
- req_queue: &Mutex<ReqQueue>,
- sender: &Sender<Message>,
- options: &RwLock<Options>,
- client_capabilities: &ClientCapabilities,
-) {
- if !client_capabilities.has_pull_configuration_support() {
- return;
- }
-
- let params = ConfigurationParams {
- items: vec![ConfigurationItem {
- section: Some("texlab".to_string()),
- scope_uri: None,
- }],
- };
-
- match send_request::<WorkspaceConfiguration>(req_queue, sender, params) {
- Ok(mut json) => {
- let value = json.pop().expect("invalid configuration request");
- let new_options = match serde_json::from_value(value) {
- Ok(new_options) => new_options,
- Err(why) => {
- warn!("Invalid configuration section \"texlab\": {}", why);
- Options::default()
- }
- };
-
- let mut options = options.write().unwrap();
- *options = new_options;
- }
- Err(why) => {
- error!("Retrieving configuration failed: {}", why);
- }
- };
-}
-
-pub fn push_config(options: &RwLock<Options>, config: serde_json::Value) {
- match serde_json::from_value(config) {
- Ok(new_options) => {
- *options.write().unwrap() = new_options;
- }
- Err(why) => {
- error!("Invalid configuration: {}", why);
- }
- };
-}
diff --git a/support/texlab/src/context.rs b/support/texlab/src/context.rs
deleted file mode 100644
index a9d6f530a6..0000000000
--- a/support/texlab/src/context.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use std::{
- path::PathBuf,
- sync::{Mutex, RwLock},
-};
-
-use lsp_types::{ClientCapabilities, ClientInfo};
-
-use crate::{
- distro::{DistributionKind, Resolver},
- Options,
-};
-
-#[derive(Debug)]
-pub struct ServerContext {
- pub current_directory: PathBuf,
- pub distro_kind: Mutex<DistributionKind>,
- pub resolver: Mutex<Resolver>,
- pub client_capabilities: Mutex<ClientCapabilities>,
- pub client_info: Mutex<Option<ClientInfo>>,
- pub options: RwLock<Options>,
-}
-
-impl ServerContext {
- pub fn new(current_dir: PathBuf) -> Self {
- Self {
- current_directory: current_dir,
- distro_kind: Mutex::new(DistributionKind::Unknown),
- resolver: Mutex::new(Resolver::default()),
- client_capabilities: Mutex::default(),
- client_info: Mutex::default(),
- options: RwLock::default(),
- }
- }
-}
diff --git a/support/texlab/src/diagnostics.rs b/support/texlab/src/diagnostics.rs
index a4adbafd7d..46e12d9cfc 100644
--- a/support/texlab/src/diagnostics.rs
+++ b/support/texlab/src/diagnostics.rs
@@ -6,11 +6,11 @@ mod latex;
use std::sync::Arc;
-use lsp_types::Diagnostic;
+use lsp_types::{Diagnostic, Url};
use multimap::MultiMap;
use rustc_hash::FxHashMap;
-use crate::{Options, Uri, Workspace};
+use crate::{Options, Workspace};
pub use self::debouncer::{DiagnosticsDebouncer, DiagnosticsMessage};
@@ -21,12 +21,12 @@ use self::{
#[derive(Default)]
pub struct DiagnosticsManager {
- static_diagnostics: FxHashMap<Arc<Uri>, MultiMap<Arc<Uri>, Diagnostic>>,
- chktex_diagnostics: MultiMap<Arc<Uri>, Diagnostic>,
+ static_diagnostics: FxHashMap<Arc<Url>, MultiMap<Arc<Url>, Diagnostic>>,
+ chktex_diagnostics: MultiMap<Arc<Url>, Diagnostic>,
}
impl DiagnosticsManager {
- pub fn update_static(&mut self, workspace: &dyn Workspace, uri: Arc<Uri>) {
+ pub fn update_static(&mut self, workspace: &Workspace, uri: Arc<Url>) {
let mut diagnostics_by_uri = MultiMap::new();
analyze_build_log_static(workspace, &mut diagnostics_by_uri, &uri);
analyze_bibtex_static(workspace, &mut diagnostics_by_uri, &uri);
@@ -34,11 +34,11 @@ impl DiagnosticsManager {
self.static_diagnostics.insert(uri, diagnostics_by_uri);
}
- pub fn update_chktex(&mut self, workspace: &dyn Workspace, uri: Arc<Uri>, options: &Options) {
+ pub fn update_chktex(&mut self, workspace: &Workspace, uri: Arc<Url>, options: &Options) {
analyze_latex_chktex(workspace, &mut self.chktex_diagnostics, &uri, options);
}
- pub fn publish(&self, uri: Arc<Uri>) -> Vec<Diagnostic> {
+ pub fn publish(&self, uri: Arc<Url>) -> Vec<Diagnostic> {
let mut all_diagnostics = Vec::new();
for diagnostics_by_uri in self.static_diagnostics.values() {
if let Some(diagnostics) = diagnostics_by_uri.get_vec(&uri) {
diff --git a/support/texlab/src/diagnostics/bibtex.rs b/support/texlab/src/diagnostics/bibtex.rs
index 8c93c6f925..1beec52c5c 100644
--- a/support/texlab/src/diagnostics/bibtex.rs
+++ b/support/texlab/src/diagnostics/bibtex.rs
@@ -1,28 +1,25 @@
use std::sync::Arc;
-use cstree::TextRange;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString};
+use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
use multimap::MultiMap;
+use rowan::{ast::AstNode, TextRange};
use crate::{
- syntax::{
- bibtex::{self, HasDelimiters, HasType},
- CstNode,
- },
- Document, LineIndexExt, Uri, Workspace,
+ syntax::bibtex::{self, HasDelims, HasEq, HasName, HasType, HasValue},
+ Document, LineIndexExt, Workspace,
};
pub fn analyze_bibtex_static(
- workspace: &dyn Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- uri: &Uri,
+ workspace: &Workspace,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ uri: &Url,
) -> Option<()> {
- let document = workspace.get(uri)?;
+ let document = workspace.documents_by_uri.get(uri)?;
let data = document.data.as_bibtex()?;
- for node in data.root.descendants() {
- analyze_entry(&document, diagnostics_by_uri, node)
- .or_else(|| analyze_field(&document, diagnostics_by_uri, node));
+ for node in bibtex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ analyze_entry(document, diagnostics_by_uri, node.clone())
+ .or_else(|| analyze_field(document, diagnostics_by_uri, node));
}
Some(())
@@ -30,17 +27,17 @@ pub fn analyze_bibtex_static(
fn analyze_entry(
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- node: &bibtex::SyntaxNode,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ node: bibtex::SyntaxNode,
) -> Option<()> {
let entry = bibtex::Entry::cast(node)?;
- if entry.left_delimiter().is_none() {
+ if entry.left_delim_token().is_none() {
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
range: document
.line_index
- .line_col_lsp_range(entry.ty()?.text_range()),
+ .line_col_lsp_range(entry.type_token()?.text_range()),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(4)),
code_description: None,
@@ -54,13 +51,13 @@ fn analyze_entry(
return Some(());
}
- if entry.key().is_none() {
+ if entry.name_token().is_none() {
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
range: document
.line_index
- .line_col_lsp_range(entry.left_delimiter()?.text_range()),
+ .line_col_lsp_range(entry.left_delim_token()?.text_range()),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(5)),
code_description: None,
@@ -74,13 +71,13 @@ fn analyze_entry(
return Some(());
}
- if entry.key().is_none() {
+ if entry.right_delim_token().is_none() {
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
range: document
.line_index
- .line_col_lsp_range(entry.right_delimiter()?.text_range()),
+ .line_col_lsp_range(TextRange::empty(entry.syntax().text_range().end())),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(6)),
code_description: None,
@@ -99,17 +96,17 @@ fn analyze_entry(
fn analyze_field(
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- node: &bibtex::SyntaxNode,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ node: bibtex::SyntaxNode,
) -> Option<()> {
let field = bibtex::Field::cast(node)?;
- if field.equality_sign().is_none() {
+ if field.eq_token().is_none() {
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
range: document
.line_index
- .line_col_lsp_range(TextRange::empty(field.name()?.text_range().end())),
+ .line_col_lsp_range(TextRange::empty(field.name_token()?.text_range().end())),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(7)),
code_description: None,
@@ -127,9 +124,9 @@ fn analyze_field(
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
- range: document.line_index.line_col_lsp_range(TextRange::empty(
- field.equality_sign()?.text_range().end(),
- )),
+ range: document
+ .line_index
+ .line_col_lsp_range(TextRange::empty(field.eq_token()?.text_range().end())),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(8)),
code_description: None,
diff --git a/support/texlab/src/diagnostics/build_log.rs b/support/texlab/src/diagnostics/build_log.rs
index 01579cbd0b..8bd83f695a 100644
--- a/support/texlab/src/diagnostics/build_log.rs
+++ b/support/texlab/src/diagnostics/build_log.rs
@@ -1,19 +1,19 @@
use std::{path::PathBuf, sync::Arc};
-use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
+use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
use multimap::MultiMap;
-use crate::{syntax::build_log::BuildErrorLevel, Uri, Workspace};
+use crate::{syntax::build_log::BuildErrorLevel, Workspace};
pub fn analyze_build_log_static(
- workspace: &dyn Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- build_log_uri: &Uri,
+ workspace: &Workspace,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ build_log_uri: &Url,
) -> Option<()> {
- let build_log_document = workspace.get(build_log_uri)?;
+ let build_log_document = workspace.documents_by_uri.get(build_log_uri)?;
let parse = build_log_document.data.as_build_log()?;
- let root_document = workspace.documents().into_iter().find(|document| {
+ let root_document = workspace.documents_by_uri.values().find(|document| {
if let Some(data) = document.data.as_latex() {
!document.uri.as_str().ends_with(".aux")
&& data
diff --git a/support/texlab/src/diagnostics/chktex.rs b/support/texlab/src/diagnostics/chktex.rs
index 1786644d97..6e7422d8b0 100644
--- a/support/texlab/src/diagnostics/chktex.rs
+++ b/support/texlab/src/diagnostics/chktex.rs
@@ -5,21 +5,21 @@ use std::{
sync::Arc,
};
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Range};
+use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Range, Url};
use multimap::MultiMap;
use once_cell::sync::Lazy;
use regex::Regex;
use tempfile::tempdir;
-use crate::{Options, RangeExt, Uri, Workspace};
+use crate::{Options, RangeExt, Workspace};
pub fn analyze_latex_chktex(
- workspace: &dyn Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- uri: &Uri,
+ workspace: &Workspace,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ uri: &Url,
options: &Options,
) -> Option<()> {
- let document = workspace.get(uri)?;
+ let document = workspace.documents_by_uri.get(uri)?;
document.data.as_latex()?;
let current_dir = options
@@ -67,14 +67,9 @@ fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> {
.current_dir(directory.path())
.output()?;
- // let mut writer = BufWriter::new(process.stdin.take().unwrap());
- // writer.write_all(text.as_bytes())?;
- // // writer.flush()?;
- // let output = process.wait_with_output()?;
-
let mut diagnostics = Vec::new();
for line in String::from_utf8_lossy(&output.stdout).lines() {
- let captures = LINE_REGEX.captures(&line).unwrap();
+ let captures = LINE_REGEX.captures(line).unwrap();
let line = captures[1].parse::<u32>().unwrap() - 1;
let character = captures[2].parse::<u32>().unwrap() - 1;
let digit = captures[3].parse::<u32>().unwrap();
diff --git a/support/texlab/src/diagnostics/debouncer.rs b/support/texlab/src/diagnostics/debouncer.rs
index 8c1cb63253..5117cf2fe0 100644
--- a/support/texlab/src/diagnostics/debouncer.rs
+++ b/support/texlab/src/diagnostics/debouncer.rs
@@ -6,13 +6,14 @@ use std::{
use crossbeam_channel::Sender;
use dashmap::DashMap;
+use lsp_types::Url;
-use crate::{Document, ServerContext, Uri, Workspace};
+use crate::{Document, Workspace};
pub enum DiagnosticsMessage {
Analyze {
- workspace: Arc<dyn Workspace>,
- document: Arc<Document>,
+ workspace: Workspace,
+ document: Document,
},
Shutdown,
}
@@ -23,28 +24,25 @@ pub struct DiagnosticsDebouncer {
}
impl DiagnosticsDebouncer {
- pub fn launch<A>(context: Arc<ServerContext>, action: A) -> Self
+ pub fn launch<A>(action: A) -> Self
where
- A: Fn(Arc<dyn Workspace>, Arc<Document>) + Send + Clone + 'static,
+ A: Fn(Workspace, Document) + Send + Clone + 'static,
{
let (sender, receiver) = crossbeam_channel::unbounded();
let handle = thread::spawn(move || {
let pool = threadpool::Builder::new().build();
- let last_task_time_by_uri: Arc<DashMap<Arc<Uri>, Instant>> = Arc::default();
+ let last_task_time_by_uri: Arc<DashMap<Arc<Url>, Instant>> = Arc::default();
while let Ok(DiagnosticsMessage::Analyze {
workspace,
document,
}) = receiver.recv()
{
- let delay = {
- context
- .options
- .read()
- .unwrap()
- .diagnostics_delay
- .unwrap_or(300)
- };
+ let delay = workspace
+ .environment
+ .options
+ .diagnostics_delay
+ .unwrap_or(300);
if let Some(time) = last_task_time_by_uri.get(&document.uri) {
if time.elapsed().as_millis() < delay as u128 {
diff --git a/support/texlab/src/diagnostics/latex.rs b/support/texlab/src/diagnostics/latex.rs
index 72048da93d..34d5e9fa29 100644
--- a/support/texlab/src/diagnostics/latex.rs
+++ b/support/texlab/src/diagnostics/latex.rs
@@ -1,29 +1,26 @@
use std::sync::Arc;
-use cstree::TextRange;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString};
+use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
use multimap::MultiMap;
+use rowan::{ast::AstNode, TextRange};
-use crate::{
- syntax::{latex, CstNode},
- Document, LineIndexExt, Uri, Workspace,
-};
+use crate::{syntax::latex, Document, LineIndexExt, Workspace};
pub fn analyze_latex_static(
- workspace: &dyn Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- uri: &Uri,
+ workspace: &Workspace,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ uri: &Url,
) -> Option<()> {
- let document = workspace.get(uri)?;
+ let document = workspace.documents_by_uri.get(uri)?;
if !document.uri.as_str().ends_with(".tex") {
return None;
}
let data = document.data.as_latex()?;
- for node in data.root.descendants() {
- analyze_environment(&document, diagnostics_by_uri, node)
- .or_else(|| analyze_curly_group(&document, diagnostics_by_uri, node))
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ analyze_environment(document, diagnostics_by_uri, node.clone())
+ .or_else(|| analyze_curly_group(document, diagnostics_by_uri, node.clone()))
.or_else(|| {
if node.kind() == latex::ERROR && node.first_token()?.text() == "}" {
diagnostics_by_uri.insert(
@@ -52,8 +49,8 @@ pub fn analyze_latex_static(
fn analyze_environment(
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- node: &latex::SyntaxNode,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ node: latex::SyntaxNode,
) -> Option<()> {
let environment = latex::Environment::cast(node)?;
let name1 = environment.begin()?.name()?.key()?;
@@ -62,7 +59,9 @@ fn analyze_environment(
diagnostics_by_uri.insert(
Arc::clone(&document.uri),
Diagnostic {
- range: document.line_index.line_col_lsp_range(name1.small_range()),
+ range: document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&name1)),
severity: Some(DiagnosticSeverity::ERROR),
code: Some(NumberOrString::Number(3)),
code_description: None,
@@ -79,8 +78,8 @@ fn analyze_environment(
fn analyze_curly_group(
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Uri>, Diagnostic>,
- node: &latex::SyntaxNode,
+ diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
+ node: latex::SyntaxNode,
) -> Option<()> {
if !matches!(
node.kind(),
diff --git a/support/texlab/src/dispatch.rs b/support/texlab/src/dispatch.rs
index a080016480..f5659ee3b4 100644
--- a/support/texlab/src/dispatch.rs
+++ b/support/texlab/src/dispatch.rs
@@ -24,9 +24,12 @@ impl NotificationDispatcher {
handler(params)?;
self.not = None;
}
- Err(not) => {
+ Err(lsp_server::ExtractError::MethodMismatch(not)) => {
self.not = Some(not);
}
+ Err(lsp_server::ExtractError::JsonError { .. }) => {
+ self.not = None;
+ }
};
}
Ok(self)
@@ -60,9 +63,12 @@ impl RequestDispatcher {
handler(id, params)?;
self.req = None;
}
- Err(req) => {
+ Err(lsp_server::ExtractError::MethodMismatch(req)) => {
self.req = Some(req);
}
+ Err(lsp_server::ExtractError::JsonError { .. }) => {
+ self.req = None;
+ }
}
}
Ok(self)
diff --git a/support/texlab/src/distro/kpsewhich.rs b/support/texlab/src/distro/kpsewhich.rs
index 2dcc98118f..ad2ac9eaf2 100644
--- a/support/texlab/src/distro/kpsewhich.rs
+++ b/support/texlab/src/distro/kpsewhich.rs
@@ -55,7 +55,7 @@ pub fn root_directories() -> Result<Vec<PathBuf>> {
let texmf = run(&["-var-value", "TEXMF"])?;
let expand_arg = format!("--expand-braces={}", texmf);
let expanded = run(&[&expand_arg])?;
- let directories = env::split_paths(&expanded.replace("!", ""))
+ let directories = env::split_paths(&expanded.replace('!', ""))
.filter(|path| path.exists())
.collect();
Ok(directories)
diff --git a/support/texlab/src/distro/texlive.rs b/support/texlab/src/distro/texlive.rs
index c9b4951e34..27f55569a7 100644
--- a/support/texlab/src/distro/texlive.rs
+++ b/support/texlab/src/distro/texlive.rs
@@ -32,8 +32,8 @@ fn parse_database(lines: Lines) -> io::Result<Vec<PathBuf>> {
let mut directory = "";
for line in lines.filter(|x| !x.trim().is_empty() && !x.starts_with('%')) {
- if line.ends_with(':') {
- directory = &line[..line.len() - 1];
+ if let Some(line) = line.strip_suffix(':') {
+ directory = line;
} else {
let path = PathBuf::from(directory).join(line);
paths.push(path);
diff --git a/support/texlab/src/workspace/document.rs b/support/texlab/src/document.rs
index a98be226c1..adb6bad5ac 100644
--- a/support/texlab/src/workspace/document.rs
+++ b/support/texlab/src/document.rs
@@ -1,6 +1,7 @@
use std::{fmt, sync::Arc};
use derive_more::From;
+use lsp_types::Url;
use crate::{
line_index::LineIndex,
@@ -8,25 +9,25 @@ use crate::{
bibtex, build_log,
latex::{self, LatexAnalyzerContext},
},
- DocumentLanguage, ServerContext, Uri,
+ DocumentLanguage, Environment,
};
#[derive(Debug, Clone)]
pub struct LatexDocumentData {
- pub root: latex::SyntaxNode,
- pub extras: latex::Extras,
+ pub green: rowan::GreenNode,
+ pub extras: Arc<latex::Extras>,
}
#[derive(Debug, Clone)]
pub struct BibtexDocumentData {
- pub root: bibtex::SyntaxNode,
+ pub green: rowan::GreenNode,
}
#[derive(Debug, Clone, From)]
pub enum DocumentData {
- Latex(LatexDocumentData),
+ Latex(Box<LatexDocumentData>),
Bibtex(BibtexDocumentData),
- BuildLog(build_log::Parse),
+ BuildLog(Arc<build_log::Parse>),
}
impl DocumentData {
@@ -65,9 +66,9 @@ impl DocumentData {
#[derive(Clone)]
pub struct Document {
- pub uri: Arc<Uri>,
- pub text: String,
- pub line_index: LineIndex,
+ pub uri: Arc<Url>,
+ pub text: Arc<String>,
+ pub line_index: Arc<LineIndex>,
pub data: DocumentData,
}
@@ -79,38 +80,45 @@ impl fmt::Debug for Document {
impl Document {
pub fn parse(
- context: Arc<ServerContext>,
- uri: Arc<Uri>,
- text: String,
+ environment: &Environment,
+ uri: Arc<Url>,
+ text: Arc<String>,
language: DocumentLanguage,
) -> Self {
- let line_index = LineIndex::new(&text);
+ let line_index = Arc::new(LineIndex::new(&text));
let data = match language {
DocumentLanguage::Latex => {
- let root = latex::parse(&text).root;
-
- let base_uri = match &context.options.read().unwrap().root_directory {
- Some(root_dir) => Uri::from_directory_path(root_dir)
- .map(Arc::new)
- .unwrap_or_else(|()| Arc::clone(&uri)),
+ let green = latex::parse(&text).green;
+ let root = latex::SyntaxNode::new_root(green.clone());
+
+ let base_uri = match &environment.options.root_directory {
+ Some(root_dir) => {
+ let root_dir = environment.current_directory.join(&root_dir);
+ Url::from_directory_path(root_dir)
+ .map(Arc::new)
+ .unwrap_or_else(|()| Arc::clone(&uri))
+ }
None => Arc::clone(&uri),
};
+
let mut context = LatexAnalyzerContext {
- inner: context,
+ environment,
extras: latex::Extras::default(),
document_uri: Arc::clone(&uri),
base_uri,
};
latex::analyze(&mut context, &root);
- let extras = context.extras;
-
- LatexDocumentData { root, extras }.into()
+ let extras = Arc::new(context.extras);
+ DocumentData::Latex(Box::new(LatexDocumentData { green, extras }))
}
DocumentLanguage::Bibtex => {
- let root = bibtex::parse(&text).root;
- BibtexDocumentData { root }.into()
+ let green = bibtex::parse(&text);
+ DocumentData::Bibtex(BibtexDocumentData { green })
+ }
+ DocumentLanguage::BuildLog => {
+ let data = Arc::new(build_log::parse(&text));
+ DocumentData::BuildLog(data)
}
- DocumentLanguage::BuildLog => DocumentData::BuildLog(build_log::parse(&text)),
};
Self {
@@ -120,8 +128,4 @@ impl Document {
data,
}
}
-
- pub fn language(&self) -> DocumentLanguage {
- self.data.language()
- }
}
diff --git a/support/texlab/src/environment.rs b/support/texlab/src/environment.rs
new file mode 100644
index 0000000000..5ba80617d5
--- /dev/null
+++ b/support/texlab/src/environment.rs
@@ -0,0 +1,32 @@
+use std::{path::PathBuf, sync::Arc};
+
+use lsp_types::{ClientCapabilities, ClientInfo};
+
+use crate::{distro::Resolver, Options};
+
+#[derive(Debug, Clone)]
+pub struct Environment {
+ pub current_directory: Arc<PathBuf>,
+ pub client_capabilities: Arc<ClientCapabilities>,
+ pub client_info: Option<Arc<ClientInfo>>,
+ pub options: Arc<Options>,
+ pub resolver: Arc<Resolver>,
+}
+
+impl Environment {
+ pub fn new(current_directory: Arc<PathBuf>) -> Self {
+ Self {
+ current_directory,
+ client_capabilities: Arc::new(ClientCapabilities::default()),
+ client_info: None,
+ options: Arc::new(Options::default()),
+ resolver: Arc::new(Resolver::default()),
+ }
+ }
+}
+
+impl Default for Environment {
+ fn default() -> Self {
+ Self::new(Arc::new(std::env::temp_dir()))
+ }
+}
diff --git a/support/texlab/src/features.rs b/support/texlab/src/features.rs
index 36c406b6b9..ed936f569f 100644
--- a/support/texlab/src/features.rs
+++ b/support/texlab/src/features.rs
@@ -12,18 +12,16 @@ mod link;
mod lsp_kinds;
mod reference;
mod rename;
-#[cfg(feature = "semantic")]
-mod semantic;
mod symbol;
use std::sync::Arc;
-use crate::{Document, ServerContext, Workspace, WorkspaceSubset};
+use lsp_types::Url;
+
+use crate::{Document, Workspace};
#[cfg(feature = "completion")]
pub use self::completion::{complete, CompletionItemData, COMPLETION_LIMIT};
-#[cfg(feature = "semantic")]
-pub use self::semantic::{find_semantic_tokens_range, legend};
pub use self::{
build::{BuildEngine, BuildParams, BuildResult, BuildStatus},
definition::goto_definition,
@@ -40,15 +38,14 @@ pub use self::{
#[derive(Clone)]
pub struct FeatureRequest<P> {
- pub context: Arc<ServerContext>,
pub params: P,
- pub workspace: Arc<dyn Workspace>,
- pub subset: WorkspaceSubset,
+ pub workspace: Workspace,
+ pub uri: Arc<Url>,
}
impl<P> FeatureRequest<P> {
pub fn main_document(&self) -> &Document {
- &self.subset.documents[0]
+ &self.workspace.documents_by_uri[&self.uri]
}
}
@@ -65,10 +62,7 @@ mod testing {
};
use typed_builder::TypedBuilder;
- use crate::{
- create_workspace_fast, distro::Resolver, DocumentLanguage, Options, ServerContext, Uri,
- Workspace, WorkspaceSource,
- };
+ use crate::{distro::Resolver, DocumentLanguage, Environment, Options, Workspace};
use super::*;
@@ -110,57 +104,52 @@ mod testing {
}
impl<'a> FeatureTester<'a> {
- pub fn uri(&self, name: &str) -> Arc<Uri> {
+ pub fn uri(&self, name: &str) -> Arc<Url> {
let path = self.current_directory.join(name);
- Arc::new(Uri::from_file_path(path).unwrap())
+ Arc::new(Url::from_file_path(path).unwrap())
}
fn options(&self) -> Options {
- let mut options = Options::default();
- options.aux_directory = self.aux_directory.clone();
- options.root_directory = self.root_directory.clone();
- options
+ Options {
+ aux_directory: self.aux_directory.clone(),
+ root_directory: self.root_directory.clone(),
+ ..Options::default()
+ }
}
fn identifier(&self) -> TextDocumentIdentifier {
- let uri = self.uri(&self.main);
- TextDocumentIdentifier::new(uri.as_ref().clone().into())
+ let uri = self.uri(self.main);
+ TextDocumentIdentifier::new(uri.as_ref().clone())
}
- fn context(&self) -> Arc<ServerContext> {
- let cx = ServerContext::new(self.current_directory.clone());
- *cx.client_capabilities.lock().unwrap() = self.client_capabilities.clone();
- *cx.client_info.lock().unwrap() = self.client_info.clone();
- *cx.options.write().unwrap() = self.options();
- *cx.resolver.lock().unwrap() = self.resolver.clone();
- Arc::new(cx)
- }
+ fn workspace(&self) -> Workspace {
+ let mut workspace = Workspace::new(Environment {
+ client_capabilities: Arc::new(self.client_capabilities.clone()),
+ client_info: self.client_info.clone().map(Arc::new),
+ options: Arc::new(self.options()),
+ resolver: Arc::new(self.resolver.clone()),
+ ..Environment::default()
+ });
- fn workspace(&self, cx: Arc<ServerContext>) -> Arc<dyn Workspace> {
- let workspace = create_workspace_fast(cx).unwrap();
for (name, source_code) in &self.files {
let uri = self.uri(name);
let path = uri.to_file_path().unwrap();
+ let text = Arc::new(source_code.trim().to_string());
let language = DocumentLanguage::by_path(&path).expect("unknown document language");
- workspace.open(
- uri,
- source_code.trim().to_string(),
- language,
- WorkspaceSource::Client,
- );
+ let document = workspace.open(uri, text, language).unwrap();
+ workspace.viewport.insert(document.uri);
}
- Arc::new(workspace)
+
+ workspace
}
fn request<P>(&self, params: P) -> FeatureRequest<P> {
- let cx = self.context();
- let workspace = self.workspace(Arc::clone(&cx));
- let subset = workspace.subset(self.uri(self.main)).unwrap();
+ let workspace = self.workspace();
+ let uri = self.uri(self.main);
FeatureRequest {
- context: cx,
- workspace,
- subset,
params,
+ workspace: workspace.slice(&uri),
+ uri,
}
}
diff --git a/support/texlab/src/features/build.rs b/support/texlab/src/features/build.rs
index 5505b9fb70..7821fac43d 100644
--- a/support/texlab/src/features/build.rs
+++ b/support/texlab/src/features/build.rs
@@ -7,21 +7,20 @@ use std::{
};
use anyhow::Result;
-use cancellation::CancellationToken;
-use crossbeam_channel::Sender;
+use crossbeam_channel::{Receiver, Sender};
use dashmap::DashMap;
use encoding_rs_io::DecodeReaderBytesBuilder;
use lsp_types::{
notification::{LogMessage, Progress},
LogMessageParams, NumberOrString, Position, ProgressParams, ProgressParamsValue,
- TextDocumentIdentifier, TextDocumentPositionParams, WorkDoneProgress, WorkDoneProgressBegin,
- WorkDoneProgressCreateParams, WorkDoneProgressEnd,
+ TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkDoneProgress,
+ WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use uuid::Uuid;
-use crate::{client, req_queue::ReqQueue, ClientCapabilitiesExt, DocumentLanguage, Uri};
+use crate::{client, req_queue::ReqQueue, ClientCapabilitiesExt, DocumentLanguage};
use super::{forward_search, FeatureRequest};
@@ -54,7 +53,7 @@ struct ProgressReporter<'a> {
}
impl<'a> ProgressReporter<'a> {
- pub fn start(&self, uri: &Uri) -> Result<()> {
+ pub fn start(&self, uri: &Url) -> Result<()> {
if self.supports_progress {
client::send_request::<lsp_types::request::WorkDoneProgressCreate>(
self.req_queue,
@@ -101,23 +100,22 @@ impl<'a> Drop for ProgressReporter<'a> {
#[derive(Default)]
pub struct BuildEngine {
lock: Mutex<()>,
- pub positions_by_uri: DashMap<Arc<Uri>, Position>,
+ pub positions_by_uri: DashMap<Arc<Url>, Position>,
}
impl BuildEngine {
pub fn build(
&self,
request: FeatureRequest<BuildParams>,
- cancellation_token: &CancellationToken,
req_queue: &Mutex<ReqQueue>,
lsp_sender: &Sender<lsp_server::Message>,
) -> Result<BuildResult> {
let lock = self.lock.lock().unwrap();
let document = request
- .subset
- .documents
- .iter()
+ .workspace
+ .documents_by_uri
+ .values()
.find(|document| {
if let Some(data) = document.data.as_latex() {
data.extras.has_document_environment
@@ -125,10 +123,9 @@ impl BuildEngine {
false
}
})
- .map(|document| document.as_ref())
.unwrap_or_else(|| request.main_document());
- if document.language() != DocumentLanguage::Latex {
+ if document.data.language() != DocumentLanguage::Latex {
return Ok(BuildResult {
status: BuildStatus::SUCCESS,
});
@@ -141,14 +138,11 @@ impl BuildEngine {
}
let path = document.uri.to_file_path().unwrap();
- let supports_progress = {
- request
- .context
- .client_capabilities
- .lock()
- .unwrap()
- .has_work_done_progress_support()
- };
+ let supports_progress = request
+ .workspace
+ .environment
+ .client_capabilities
+ .has_work_done_progress_support();
let token = format!("texlab-build-{}", Uuid::new_v4());
let progress_reporter = ProgressReporter {
@@ -159,7 +153,7 @@ impl BuildEngine {
};
progress_reporter.start(&document.uri)?;
- let options = { request.context.options.read().unwrap().clone() };
+ let options = &request.workspace.environment.options;
let build_dir = options
.root_directory
@@ -183,8 +177,12 @@ impl BuildEngine {
.current_dir(build_dir)
.spawn()?;
- let log_handle = capture_output(&mut process, lsp_sender);
+ let (exit_sender, exit_receiver) = crossbeam_channel::bounded(1);
+ let log_handle = capture_output(&mut process, lsp_sender, exit_receiver);
let success = process.wait().map(|status| status.success())?;
+ exit_sender.send(())?;
+ drop(exit_sender);
+
log_handle.join().unwrap();
let status = if success {
BuildStatus::SUCCESS
@@ -201,17 +199,14 @@ impl BuildEngine {
position: self
.positions_by_uri
.get(&request.main_document().uri)
- .map(|guard| guard.clone())
+ .map(|guard| *guard)
.unwrap_or_default(),
- text_document: TextDocumentIdentifier::new(
- request.main_document().uri.as_ref().clone().into(),
- ),
+ text_document: TextDocumentIdentifier::new(request.uri.as_ref().clone()),
},
- context: request.context,
+ uri: request.uri,
workspace: request.workspace,
- subset: request.subset,
};
- forward_search::execute_forward_search(request, cancellation_token);
+ forward_search::execute_forward_search(request);
}
Ok(BuildResult { status })
@@ -221,26 +216,29 @@ impl BuildEngine {
fn capture_output(
process: &mut std::process::Child,
lsp_sender: &Sender<lsp_server::Message>,
+ exit_receiver: Receiver<()>,
) -> JoinHandle<()> {
let (log_sender, log_receiver) = crossbeam_channel::unbounded();
track_output(process.stdout.take().unwrap(), log_sender.clone());
track_output(process.stderr.take().unwrap(), log_sender);
- let log_handle = {
- let lsp_sender = lsp_sender.clone();
- thread::spawn(move || {
- for message in &log_receiver {
- client::send_notification::<LogMessage>(
- &lsp_sender,
- LogMessageParams {
- message,
- typ: lsp_types::MessageType::LOG,
- },
- )
- .unwrap();
- }
- })
- };
- log_handle
+ let lsp_sender = lsp_sender.clone();
+ thread::spawn(move || loop {
+ crossbeam_channel::select! {
+ recv(&log_receiver) -> message => {
+ if let Ok(message) = message {
+ client::send_notification::<LogMessage>(
+ &lsp_sender,
+ LogMessageParams {
+ message,
+ typ: lsp_types::MessageType::LOG,
+ },
+ )
+ .unwrap();
+ }
+ },
+ recv(&exit_receiver) -> _ => break,
+ };
+ })
}
fn replace_placeholder(arg: String, file: &Path) -> String {
diff --git a/support/texlab/src/features/completion.rs b/support/texlab/src/features/completion.rs
index 2a3f4ea9af..3b5187f8eb 100644
--- a/support/texlab/src/features/completion.rs
+++ b/support/texlab/src/features/completion.rs
@@ -21,18 +21,20 @@ mod util;
use std::borrow::Cow;
-use cancellation::CancellationToken;
-use cstree::TextSize;
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use itertools::Itertools;
use lsp_types::{
CompletionItem, CompletionList, CompletionParams, CompletionTextEdit, Documentation,
InsertTextFormat, MarkupContent, MarkupKind, TextEdit,
};
+use rowan::{ast::AstNode, TextSize};
use rustc_hash::FxHashSet;
use crate::{
- syntax::{bibtex, latex, CstNode},
+ syntax::{
+ bibtex::{self},
+ latex,
+ },
LineIndexExt,
};
@@ -69,39 +71,41 @@ use super::{
pub const COMPLETION_LIMIT: usize = 50;
-pub fn complete(
- request: FeatureRequest<CompletionParams>,
- cancellation_token: &CancellationToken,
-) -> Option<CompletionList> {
+pub fn complete(request: FeatureRequest<CompletionParams>) -> Option<CompletionList> {
let mut items = Vec::new();
let context = CursorContext::new(request);
log::debug!("[Completion] Cursor: {:?}", context.cursor);
- complete_entry_types(&context, &mut items, cancellation_token);
- complete_fields(&context, &mut items, cancellation_token);
- complete_arguments(&context, &mut items, cancellation_token);
- complete_citations(&context, &mut items, cancellation_token);
- complete_imports(&context, &mut items, cancellation_token);
- complete_colors(&context, &mut items, cancellation_token);
- complete_color_models(&context, &mut items, cancellation_token);
- complete_acronyms(&context, &mut items, cancellation_token);
- complete_glossary_entries(&context, &mut items, cancellation_token);
- complete_includes(&context, &mut items, cancellation_token);
- complete_labels(&context, &mut items, cancellation_token);
- complete_tikz_libraries(&context, &mut items, cancellation_token);
- complete_component_environments(&context, &mut items, cancellation_token);
- complete_theorem_environments(&context, &mut items, cancellation_token);
- complete_user_environments(&context, &mut items, cancellation_token);
- complete_begin_command(&context, &mut items, cancellation_token);
- complete_component_commands(&context, &mut items, cancellation_token);
- complete_user_commands(&context, &mut items, cancellation_token);
-
- cancellation_token.result().ok()?;
+ complete_entry_types(&context, &mut items);
+ complete_fields(&context, &mut items);
+ complete_arguments(&context, &mut items);
+ complete_citations(&context, &mut items);
+ complete_imports(&context, &mut items);
+ complete_colors(&context, &mut items);
+ complete_color_models(&context, &mut items);
+ complete_acronyms(&context, &mut items);
+ complete_glossary_entries(&context, &mut items);
+ complete_includes(&context, &mut items);
+ complete_labels(&context, &mut items);
+ complete_tikz_libraries(&context, &mut items);
+ complete_component_environments(&context, &mut items);
+ complete_theorem_environments(&context, &mut items);
+ complete_user_environments(&context, &mut items);
+ complete_begin_command(&context, &mut items);
+ complete_component_commands(&context, &mut items);
+ complete_user_commands(&context, &mut items);
let mut items = dedup(items);
preselect(&context, &mut items);
score(&context, &mut items);
- items.sort_by_key(|item| (!item.preselect, -item.score.unwrap_or(std::i64::MIN + 1)));
+ items.sort_by(|a, b| {
+ a.preselect
+ .cmp(&b.preselect)
+ .reverse()
+ .then_with(|| a.score.cmp(&b.score).reverse())
+ .then_with(|| a.data.label().cmp(b.data.label()))
+ });
+
let items: Vec<_> = items
.into_iter()
.take(COMPLETION_LIMIT)
@@ -113,14 +117,12 @@ pub fn complete(
let is_incomplete = if context
.request
- .context
+ .workspace
+ .environment
.client_info
- .lock()
- .unwrap()
.as_ref()
- .map(|info| info.name.as_str())
- .unwrap_or_default()
- == "Visual Studio Code"
+ .as_ref()
+ .map_or(false, |info| info.name.as_str() == "Visual Studio Code")
{
true
} else {
@@ -159,10 +161,10 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp
}
}
Cursor::Latex(token) if token.kind() == latex::WORD => {
- if let Some(key) = latex::Key::cast(token.parent()) {
+ if let Some(key) = token.parent().and_then(latex::Key::cast) {
key.words()
.take_while(|word| word.text_range() != token.text_range())
- .chain(std::iter::once(token))
+ .chain(std::iter::once(token.clone()))
.filter(|word| word.text_range().start() < context.offset)
.join(" ")
.into()
@@ -171,16 +173,17 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp
}
}
Cursor::Latex(_) => "".into(),
- Cursor::Bibtex(token) if token.kind().is_type() => token.text().into(),
- Cursor::Bibtex(token) if token.kind() == bibtex::WORD => {
- if let Some(key) = bibtex::Key::cast(token.parent()) {
- key.to_string().into()
- } else {
- token.text().into()
- }
- }
- Cursor::Bibtex(token) if token.kind() == bibtex::COMMAND_NAME => {
- token.text().trim_end().into()
+ Cursor::Bibtex(token)
+ if matches!(
+ token.kind(),
+ bibtex::TYPE
+ | bibtex::NAME
+ | bibtex::WORD
+ | bibtex::COMMAND_NAME
+ | bibtex::ACCENT_NAME
+ ) =>
+ {
+ token.text().into()
}
Cursor::Bibtex(_) => "".into(),
Cursor::Nothing => "".into(),
@@ -200,36 +203,32 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp
matcher.fuzzy_match(name, &pattern)
}
InternalCompletionItemData::BeginCommand => matcher.fuzzy_match("begin", &pattern[1..]),
- InternalCompletionItemData::Citation { key, .. } => matcher.fuzzy_match(&key, &pattern),
+ InternalCompletionItemData::Citation { key, .. } => matcher.fuzzy_match(key, &pattern),
InternalCompletionItemData::ComponentCommand { name, .. } => {
matcher.fuzzy_match(name, &pattern[1..])
}
InternalCompletionItemData::ComponentEnvironment { name, .. } => {
matcher.fuzzy_match(name, &pattern)
}
- InternalCompletionItemData::Class { name } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::Package { name } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::Color { name } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::ColorModel { name } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::Acronym { name } => matcher.fuzzy_match(&name, &pattern),
+ InternalCompletionItemData::Class { name } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::Package { name } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::Color { name } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::ColorModel { name } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::Acronym { name } => matcher.fuzzy_match(name, &pattern),
InternalCompletionItemData::GlossaryEntry { name } => {
- matcher.fuzzy_match(&name, &pattern)
+ matcher.fuzzy_match(name, &pattern)
}
- InternalCompletionItemData::File { name } => matcher.fuzzy_match(&name, file_pattern),
+ InternalCompletionItemData::File { name } => matcher.fuzzy_match(name, file_pattern),
InternalCompletionItemData::Directory { name } => {
- matcher.fuzzy_match(&name, file_pattern)
- }
- InternalCompletionItemData::Label { name, .. } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::UserCommand { name } => {
- matcher.fuzzy_match(&name, &pattern[1..])
+ matcher.fuzzy_match(name, file_pattern)
}
+ InternalCompletionItemData::Label { name, .. } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::UserCommand { name } => matcher.fuzzy_match(name, &pattern),
InternalCompletionItemData::UserEnvironment { name } => {
- matcher.fuzzy_match(&name, &pattern)
- }
- InternalCompletionItemData::PgfLibrary { name } => matcher.fuzzy_match(&name, &pattern),
- InternalCompletionItemData::TikzLibrary { name } => {
- matcher.fuzzy_match(&name, &pattern)
+ matcher.fuzzy_match(name, &pattern)
}
+ InternalCompletionItemData::PgfLibrary { name } => matcher.fuzzy_match(name, &pattern),
+ InternalCompletionItemData::TikzLibrary { name } => matcher.fuzzy_match(name, &pattern),
};
}
}
@@ -239,7 +238,7 @@ fn preselect(
items: &mut [InternalCompletionItem],
) -> Option<()> {
let name = context.cursor.as_latex()?;
- let group = latex::CurlyGroupWord::cast(name.parent())?;
+ let group = latex::CurlyGroupWord::cast(name.parent()?)?;
let end = latex::End::cast(group.syntax().parent()?)?;
let environment = latex::Environment::cast(end.syntax().parent()?)?;
let name = environment.begin()?.name()?.key()?.to_string();
@@ -308,17 +307,16 @@ fn convert_internal_items(
data: Some(serde_json::to_value(CompletionItemData::Argument).unwrap()),
text_edit: Some(CompletionTextEdit::Edit(text_edit)),
documentation: image
- .and_then(|image| image_documentation(&context.request, &name, image)),
+ .and_then(|image| image_documentation(&context.request, name, image)),
..CompletionItem::default()
}
}
InternalCompletionItemData::BeginCommand => {
if context
.request
- .context
+ .workspace
+ .environment
.client_capabilities
- .lock()
- .unwrap()
.text_document
.as_ref()
.and_then(|cap| cap.completion.as_ref())
@@ -379,7 +377,7 @@ fn convert_internal_items(
|glyph| format!("{}, {}", glyph, component_detail(file_names)),
);
let documentation =
- image.and_then(|img| image_documentation(&context.request, &name, img));
+ image.and_then(|img| image_documentation(&context.request, name, img));
let text_edit = TextEdit::new(range, name.to_string());
CompletionItem {
kind: Some(adjust_kind(
@@ -459,7 +457,7 @@ fn convert_internal_items(
InternalCompletionItemData::Acronym { name } => {
let text_edit = TextEdit::new(range, name.to_string());
CompletionItem {
- label: name.into(),
+ label: name,
kind: Some(adjust_kind(
&context.request,
Structure::GlossaryEntry.completion_kind(),
@@ -472,7 +470,7 @@ fn convert_internal_items(
InternalCompletionItemData::GlossaryEntry { name } => {
let text_edit = TextEdit::new(range, name.to_string());
CompletionItem {
- label: name.into(),
+ label: name,
kind: Some(adjust_kind(
&context.request,
Structure::GlossaryEntry.completion_kind(),
@@ -517,7 +515,7 @@ fn convert_internal_items(
} => {
let text_edit = TextEdit::new(range, name.to_string());
CompletionItem {
- label: name.into(),
+ label: name,
kind: Some(adjust_kind(&context.request, kind.completion_kind())),
detail: header,
documentation: footer.map(Documentation::String),
@@ -530,6 +528,7 @@ fn convert_internal_items(
}
InternalCompletionItemData::UserCommand { name } => {
let detail = "user-defined".into();
+ let name = &name[1..];
let text_edit = TextEdit::new(range, name.to_string());
CompletionItem {
kind: Some(adjust_kind(
@@ -543,7 +542,7 @@ fn convert_internal_items(
}
InternalCompletionItemData::UserEnvironment { name } => {
let detail = "user-defined".into();
- let text_edit = TextEdit::new(range, name.to_string());
+ let text_edit = TextEdit::new(range, name.clone());
CompletionItem {
kind: Some(adjust_kind(
&context.request,
@@ -551,7 +550,7 @@ fn convert_internal_items(
)),
data: Some(serde_json::to_value(CompletionItemData::Environment).unwrap()),
text_edit: Some(CompletionTextEdit::Edit(text_edit)),
- ..CompletionItem::new_simple(name.into(), detail)
+ ..CompletionItem::new_simple(name, detail)
}
}
InternalCompletionItemData::PgfLibrary { name } => {
diff --git a/support/texlab/src/features/completion/acronym_ref.rs b/support/texlab/src/features/completion/acronym_ref.rs
index db3e34f9eb..2bb931fde4 100644
--- a/support/texlab/src/features/completion/acronym_ref.rs
+++ b/support/texlab/src/features/completion/acronym_ref.rs
@@ -1,27 +1,20 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_acronyms<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (_, range, group) = context.find_curly_group_word()?;
latex::AcronymReference::cast(group.syntax().parent()?)?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- for name in data
- .root
+ for name in latex::SyntaxNode::new_root(data.green.clone())
.descendants()
.filter_map(latex::AcronymDefinition::cast)
.filter_map(|node| node.name())
@@ -40,7 +33,7 @@ pub fn complete_acronyms<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -58,7 +51,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -75,7 +68,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -92,7 +85,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -112,7 +105,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -132,7 +125,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -149,7 +142,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_acronyms(&context, &mut actual_items, CancellationToken::none());
+ complete_acronyms(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/argument.rs b/support/texlab/src/features/completion/argument.rs
index e2637e026f..ded16b359e 100644
--- a/support/texlab/src/features/completion/argument.rs
+++ b/support/texlab/src/features/completion/argument.rs
@@ -1,22 +1,14 @@
-use cancellation::CancellationToken;
-use cstree::TextRange;
use lsp_types::CompletionParams;
+use rowan::{ast::AstNode, TextRange};
-use crate::{
- component_db::COMPONENT_DATABASE,
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{component_db::COMPONENT_DATABASE, features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_arguments<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let token = context.cursor.as_latex()?;
let range = if token.kind() == latex::WORD {
@@ -25,8 +17,13 @@ pub fn complete_arguments<'a>(
TextRange::empty(context.offset)
};
- let group = latex::CurlyGroup::cast(token.parent())
- .or_else(|| token.parent().parent().and_then(latex::CurlyGroup::cast))
+ let group = latex::CurlyGroup::cast(token.parent()?)
+ .or_else(|| {
+ token
+ .parent()
+ .and_then(|node| node.parent())
+ .and_then(latex::CurlyGroup::cast)
+ })
.filter(|group| context.is_inside_latex_curly(group))?;
let command = latex::GenericCommand::cast(group.syntax().parent()?)?;
@@ -37,9 +34,10 @@ pub fn complete_arguments<'a>(
.filter_map(latex::CurlyGroup::cast)
.position(|g| g.syntax().text_range() == group.syntax().text_range())?;
- let command_name = &command.name()?.text()[1..];
+ let command_name = command.name()?;
+ let command_name = &command_name.text()[1..];
- for component in COMPONENT_DATABASE.linked_components(&context.request.subset) {
+ for component in COMPONENT_DATABASE.linked_components(&context.request.workspace) {
for component_command in component
.commands
.iter()
@@ -70,7 +68,7 @@ pub fn complete_arguments<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -88,7 +86,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -105,7 +103,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -122,7 +120,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -142,7 +140,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -162,7 +160,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -182,7 +180,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_arguments(&context, &mut actual_items, CancellationToken::none());
+ complete_arguments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
diff --git a/support/texlab/src/features/completion/begin_command.rs b/support/texlab/src/features/completion/begin_command.rs
index ebd272711e..b9b4f06117 100644
--- a/support/texlab/src/features/completion/begin_command.rs
+++ b/support/texlab/src/features/completion/begin_command.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::features::cursor::CursorContext;
@@ -8,9 +7,7 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_begin_command(
context: &CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
let range = context.cursor.command_range(context.offset)?;
items.push(InternalCompletionItem::new(
@@ -22,7 +19,7 @@ pub fn complete_begin_command(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -40,7 +37,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_begin_command(&context, &mut actual_items, CancellationToken::none());
+ complete_begin_command(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -57,7 +54,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_begin_command(&context, &mut actual_items, CancellationToken::none());
+ complete_begin_command(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -74,7 +71,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_begin_command(&context, &mut actual_items, CancellationToken::none());
+ complete_begin_command(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/citation.rs b/support/texlab/src/features/completion/citation.rs
index 0a7cc26b10..92b21f0ffc 100644
--- a/support/texlab/src/features/completion/citation.rs
+++ b/support/texlab/src/features/completion/citation.rs
@@ -1,16 +1,15 @@
use std::sync::Arc;
-use cancellation::CancellationToken;
-use cstree::TextRange;
use lsp_types::CompletionParams;
use once_cell::sync::Lazy;
use regex::Regex;
+use rowan::{ast::AstNode, TextRange};
use crate::{
features::{cursor::CursorContext, lsp_kinds::Structure},
syntax::{
- bibtex::{self, HasType},
- latex, CstNode,
+ bibtex::{self, HasName, HasType},
+ latex,
},
BibtexEntryTypeCategory, Document, LANGUAGE_DATA,
};
@@ -20,24 +19,29 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_citations<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
let token = context.cursor.as_latex()?;
let range = if token.kind() == latex::WORD {
- latex::Key::cast(token.parent())
- .map(|key| key.small_range())
- .or_else(|| latex::Text::cast(token.parent()).map(|text| text.small_range()))?
+ latex::Key::cast(token.parent()?)
+ .map(|key| latex::small_range(&key))
+ .or_else(|| {
+ token
+ .parent()
+ .and_then(latex::Text::cast)
+ .map(|text| latex::small_range(&text))
+ })?
} else {
TextRange::empty(context.offset)
};
check_citation(context).or_else(|| check_acronym(context))?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_bibtex() {
- for entry in data.root.children().filter_map(bibtex::Entry::cast) {
- cancellation_token.result().ok()?;
+ for entry in bibtex::SyntaxNode::new_root(data.green.clone())
+ .children()
+ .filter_map(bibtex::Entry::cast)
+ {
if let Some(item) = make_item(document, entry, range) {
items.push(item);
}
@@ -66,14 +70,14 @@ fn check_acronym(context: &CursorContext<CompletionParams>) -> Option<()> {
Some(())
}
-fn make_item<'a>(
- document: &'a Document,
- entry: bibtex::Entry<'a>,
+fn make_item(
+ document: &Document,
+ entry: bibtex::Entry,
range: TextRange,
-) -> Option<InternalCompletionItem<'a>> {
- let key = entry.key()?.to_string();
+) -> Option<InternalCompletionItem> {
+ let key = entry.name_token()?.to_string();
let ty = LANGUAGE_DATA
- .find_entry_type(&entry.ty()?.text()[1..])
+ .find_entry_type(&entry.type_token()?.text()[1..])
.map(|ty| Structure::Entry(ty.category))
.unwrap_or_else(|| Structure::Entry(BibtexEntryTypeCategory::Misc));
@@ -87,7 +91,7 @@ fn make_item<'a>(
.replace('{', "")
.replace('}', "")
.replace(',', " ")
- .replace("=", " "),
+ .replace('=', " "),
" "
)
.trim(),
@@ -124,7 +128,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -141,7 +145,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -161,7 +165,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -184,7 +188,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -207,7 +211,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -230,7 +234,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -256,7 +260,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -282,7 +286,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_citations(&context, &mut actual_items, CancellationToken::none());
+ complete_citations(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/color.rs b/support/texlab/src/features/completion/color.rs
index 15a7c341f5..1d241d2c94 100644
--- a/support/texlab/src/features/completion/color.rs
+++ b/support/texlab/src/features/completion/color.rs
@@ -1,21 +1,14 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
- LANGUAGE_DATA,
-};
+use crate::{features::cursor::CursorContext, syntax::latex, LANGUAGE_DATA};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_colors<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (_, range, group) = context.find_curly_group_word()?;
latex::ColorReference::cast(group.syntax().parent()?)?;
@@ -31,7 +24,7 @@ pub fn complete_colors<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -49,7 +42,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_colors(&context, &mut actual_items, CancellationToken::none());
+ complete_colors(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -66,7 +59,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_colors(&context, &mut actual_items, CancellationToken::none());
+ complete_colors(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -83,7 +76,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_colors(&context, &mut actual_items, CancellationToken::none());
+ complete_colors(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -103,7 +96,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_colors(&context, &mut actual_items, CancellationToken::none());
+ complete_colors(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/color_model.rs b/support/texlab/src/features/completion/color_model.rs
index a24953caa5..2190b690ad 100644
--- a/support/texlab/src/features/completion/color_model.rs
+++ b/support/texlab/src/features/completion/color_model.rs
@@ -1,11 +1,7 @@
-use cancellation::CancellationToken;
-use cstree::TextRange;
use lsp_types::CompletionParams;
+use rowan::{ast::AstNode, TextRange};
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
@@ -14,10 +10,7 @@ const MODEL_NAMES: &[&str] = &["gray", "rgb", "RGB", "HTML", "cmyk"];
pub fn complete_color_models<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let range = check_color_definition(context).or_else(|| check_color_definition_set(context))?;
for name in MODEL_NAMES {
@@ -51,7 +44,7 @@ fn check_color_definition_set(context: &CursorContext<CompletionParams>) -> Opti
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -69,7 +62,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -86,7 +79,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -103,7 +96,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -123,7 +116,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -143,7 +136,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -163,7 +156,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_color_models(&context, &mut actual_items, CancellationToken::none());
+ complete_color_models(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/component_command.rs b/support/texlab/src/features/completion/component_command.rs
index aba5a78597..b93a782512 100644
--- a/support/texlab/src/features/completion/component_command.rs
+++ b/support/texlab/src/features/completion/component_command.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::{component_db::COMPONENT_DATABASE, features::cursor::CursorContext};
@@ -8,14 +7,10 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_component_commands<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let range = context.cursor.command_range(context.offset)?;
- for component in COMPONENT_DATABASE.linked_components(&context.request.subset) {
- cancellation_token.result().ok()?;
+ for component in COMPONENT_DATABASE.linked_components(&context.request.workspace) {
for command in &component.commands {
items.push(InternalCompletionItem::new(
range,
@@ -34,7 +29,7 @@ pub fn complete_component_commands<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -52,7 +47,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -69,7 +64,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -86,7 +81,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -106,7 +101,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -123,7 +118,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in &actual_items {
@@ -147,7 +142,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -167,7 +162,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_component_commands(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/component_environment.rs b/support/texlab/src/features/completion/component_environment.rs
index d4340a160a..057e98e491 100644
--- a/support/texlab/src/features/completion/component_environment.rs
+++ b/support/texlab/src/features/completion/component_environment.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::{component_db::COMPONENT_DATABASE, features::cursor::CursorContext};
@@ -8,14 +7,10 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_component_environments<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (_, range) = context.find_environment_name()?;
- for component in COMPONENT_DATABASE.linked_components(&context.request.subset) {
- cancellation_token.result().ok()?;
+ for component in COMPONENT_DATABASE.linked_components(&context.request.workspace) {
for name in &component.environments {
items.push(InternalCompletionItem::new(
range,
@@ -32,7 +27,7 @@ pub fn complete_component_environments<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -50,7 +45,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -67,7 +62,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -84,7 +79,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -104,7 +99,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -124,7 +119,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in &actual_items {
@@ -148,11 +143,31 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_component_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_component_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
assert_eq!(item.range, TextRange::new(7.into(), 8.into()));
}
}
+
+ #[test]
+ fn test_command_definition() {
+ let request = FeatureTester::builder()
+ .files(vec![("main.tex", "\\newcommand{\\foo}{\\begin{\nd}")])
+ .main("main.tex")
+ .line(1)
+ .character(1)
+ .build()
+ .completion();
+
+ let context = CursorContext::new(request);
+ let mut actual_items = Vec::new();
+ complete_component_environments(&context, &mut actual_items);
+
+ assert!(!actual_items.is_empty());
+ for item in actual_items {
+ assert_eq!(item.range, TextRange::new(26.into(), 27.into()));
+ }
+ }
}
diff --git a/support/texlab/src/features/completion/entry_type.rs b/support/texlab/src/features/completion/entry_type.rs
index c8774ea3ff..06180dd8cc 100644
--- a/support/texlab/src/features/completion/entry_type.rs
+++ b/support/texlab/src/features/completion/entry_type.rs
@@ -1,26 +1,20 @@
-use cancellation::CancellationToken;
-use cstree::{TextRange, TextSize};
use lsp_types::CompletionParams;
+use rowan::{TextRange, TextSize};
-use crate::{features::cursor::CursorContext, LANGUAGE_DATA};
+use crate::{features::cursor::CursorContext, syntax::bibtex, LANGUAGE_DATA};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_entry_types<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- if cancellation_token.is_canceled() {
- return None;
- }
-
let range = context
.cursor
.as_bibtex()
- .filter(|token| token.kind().is_type())
- .filter(|token| token.text_range().start() != context.offset)
+ .filter(|token| token.kind() == bibtex::TYPE)
.map(|token| token.text_range())
+ .filter(|range| range.start() != context.offset)
.map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))?;
for ty in &LANGUAGE_DATA.entry_types {
@@ -50,7 +44,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -67,7 +61,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -84,7 +78,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -104,7 +98,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -124,7 +118,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -144,7 +138,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -164,7 +158,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -184,7 +178,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -204,7 +198,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -224,7 +218,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_entry_types(&context, &mut actual_items, CancellationToken::none());
+ complete_entry_types(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
diff --git a/support/texlab/src/features/completion/field.rs b/support/texlab/src/features/completion/field.rs
index ad1042b04d..051b8d69b5 100644
--- a/support/texlab/src/features/completion/field.rs
+++ b/support/texlab/src/features/completion/field.rs
@@ -1,10 +1,9 @@
-use cancellation::CancellationToken;
-use cstree::TextRange;
use lsp_types::CompletionParams;
+use rowan::{ast::AstNode, TextRange};
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, CstNode},
+ syntax::bibtex::{self, HasName},
LANGUAGE_DATA,
};
@@ -13,20 +12,18 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_fields<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let token = context.cursor.as_bibtex()?;
- let range = if token.kind() == bibtex::WORD {
+
+ let range = if token.kind() == bibtex::NAME {
token.text_range()
} else {
TextRange::empty(context.offset)
};
- let parent = token.parent();
- if let Some(entry) = bibtex::Entry::cast(parent) {
- if entry.key()?.small_range() == token.text_range() {
+ let parent = token.parent()?;
+ if let Some(entry) = bibtex::Entry::cast(parent.clone()) {
+ if entry.name_token()?.text_range() == token.text_range() {
return None;
}
} else {
@@ -59,7 +56,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -76,7 +73,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -93,7 +90,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -113,7 +110,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -133,7 +130,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -153,7 +150,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -170,7 +167,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -190,7 +187,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_fields(&context, &mut actual_items, CancellationToken::none());
+ complete_fields(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/glossary_ref.rs b/support/texlab/src/features/completion/glossary_ref.rs
index 66d20e46ff..6805108f16 100644
--- a/support/texlab/src/features/completion/glossary_ref.rs
+++ b/support/texlab/src/features/completion/glossary_ref.rs
@@ -1,29 +1,21 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_glossary_entries<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (_, range, group) = context.find_curly_group_word()?;
latex::GlossaryEntryReference::cast(group.syntax().parent()?)?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- for node in data.root.descendants() {
- cancellation_token.result().ok()?;
-
- if let Some(name) = latex::GlossaryEntryDefinition::cast(node)
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if let Some(name) = latex::GlossaryEntryDefinition::cast(node.clone())
.and_then(|entry| entry.name())
.and_then(|name| name.key())
.map(|name| name.to_string())
@@ -51,7 +43,7 @@ pub fn complete_glossary_entries<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -69,7 +61,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_glossary_entries(&context, &mut actual_items, CancellationToken::none());
+ complete_glossary_entries(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -86,7 +78,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_glossary_entries(&context, &mut actual_items, CancellationToken::none());
+ complete_glossary_entries(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -103,7 +95,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_glossary_entries(&context, &mut actual_items, CancellationToken::none());
+ complete_glossary_entries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -123,7 +115,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_glossary_entries(&context, &mut actual_items, CancellationToken::none());
+ complete_glossary_entries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/import.rs b/support/texlab/src/features/completion/import.rs
index da4c12c02b..6e3a8deeaa 100644
--- a/support/texlab/src/features/completion/import.rs
+++ b/support/texlab/src/features/completion/import.rs
@@ -1,20 +1,15 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
+use rowan::ast::AstNode;
use rustc_hash::FxHashSet;
use smol_str::SmolStr;
-use crate::{
- component_db::COMPONENT_DATABASE,
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{component_db::COMPONENT_DATABASE, features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_imports<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
let (_, range, group) = context.find_curly_group_word_list()?;
@@ -40,7 +35,6 @@ pub fn complete_imports<'a>(
.flat_map(|comp| comp.file_names.iter())
.filter(|file_name| file_name.ends_with(extension))
{
- cancellation_token.result().ok()?;
file_names.insert(file_name);
let stem = &file_name[0..file_name.len() - 4];
let data = factory(stem.into());
@@ -48,14 +42,12 @@ pub fn complete_imports<'a>(
items.push(item);
}
- let resolver = context.request.context.resolver.lock().unwrap();
+ let resolver = &context.request.workspace.environment.resolver;
for file_name in resolver
.files_by_name
.keys()
.filter(|file_name| file_name.ends_with(extension) && !file_names.contains(file_name))
{
- cancellation_token.result().ok()?;
-
let stem = &file_name[0..file_name.len() - 4];
let data = factory(stem.into());
let item = InternalCompletionItem::new(range, data);
@@ -67,7 +59,7 @@ pub fn complete_imports<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -85,7 +77,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -102,7 +94,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -119,7 +111,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -139,7 +131,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -159,7 +151,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -179,7 +171,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_imports(&context, &mut actual_items, CancellationToken::none());
+ complete_imports(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/include.rs b/support/texlab/src/features/completion/include.rs
index 4a8e561866..7f6767b89f 100644
--- a/support/texlab/src/features/completion/include.rs
+++ b/support/texlab/src/features/completion/include.rs
@@ -4,23 +4,17 @@ use std::{
path::{Path, PathBuf},
};
-use cancellation::CancellationToken;
-use cstree::{TextRange, TextSize};
use lsp_types::CompletionParams;
+use rowan::{ast::AstNode, TextRange, TextSize};
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
-};
+use crate::{features::cursor::CursorContext, syntax::latex};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_includes<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
if context.request.main_document().uri.scheme() != "file" {
return None;
}
@@ -32,7 +26,7 @@ pub fn complete_includes<'a>(
latex::PACKAGE_INCLUDE => (false, &["sty"]),
latex::CLASS_INCLUDE => (false, &["cls"]),
latex::LATEX_INCLUDE => {
- let include = latex::Include::cast(include)?;
+ let include = latex::Include::cast(include.clone())?;
(
matches!(include.command()?.text(), "\\input" | "\\subfile"),
&["tex"],
@@ -55,8 +49,24 @@ pub fn complete_includes<'a>(
TextRange::new(start, path_range.end())
};
- let current_dir = current_dir(context, &path_text)?;
- for entry in fs::read_dir(current_dir).ok()?.filter_map(Result::ok) {
+ let mut dirs = vec![current_dir(context, &path_text, None)];
+ if include.kind() == latex::GRAPHICS_INCLUDE {
+ for document in context.request.workspace.documents_by_uri.values() {
+ if let Some(data) = document.data.as_latex() {
+ for graphics_path in &data.extras.graphics_paths {
+ dirs.push(current_dir(context, &path_text, Some(graphics_path)));
+ }
+ }
+ }
+ }
+
+ for entry in dirs
+ .into_iter()
+ .flatten()
+ .filter_map(|dir| fs::read_dir(dir).ok())
+ .flatten()
+ .flatten()
+ {
let mut path = entry.path();
let file_type = entry.file_type().ok()?;
@@ -79,19 +89,23 @@ pub fn complete_includes<'a>(
Some(())
}
-fn current_dir(context: &CursorContext<CompletionParams>, path_text: &str) -> Option<PathBuf> {
+fn current_dir(
+ context: &CursorContext<CompletionParams>,
+ path_text: &str,
+ graphics_path: Option<&str>,
+) -> Option<PathBuf> {
let mut path = context
.request
- .context
+ .workspace
+ .environment
.options
- .read()
- .unwrap()
.root_directory
.as_ref()
.map(|root_directory| {
context
.request
- .context
+ .workspace
+ .environment
.current_directory
.join(root_directory)
})
@@ -103,6 +117,10 @@ fn current_dir(context: &CursorContext<CompletionParams>, path_text: &str) -> Op
path = PathBuf::from(path.to_str()?.replace('\\', "/"));
if !path_text.is_empty() {
+ if let Some(graphics_path) = graphics_path {
+ path.push(graphics_path);
+ }
+
path.push(&path_text);
if !path_text.ends_with('/') {
path.pop();
@@ -150,7 +168,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_includes(&context, &mut actual_items, CancellationToken::none());
+ complete_includes(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -167,7 +185,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_includes(&context, &mut actual_items, CancellationToken::none());
+ complete_includes(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
diff --git a/support/texlab/src/features/completion/label.rs b/support/texlab/src/features/completion/label.rs
index 3efb168d0d..e6c02db565 100644
--- a/support/texlab/src/features/completion/label.rs
+++ b/support/texlab/src/features/completion/label.rs
@@ -1,11 +1,10 @@
-use cancellation::CancellationToken;
-use cstree::TextRange;
use lsp_types::CompletionParams;
+use rowan::{ast::AstNode, TextRange};
use crate::{
features::{cursor::CursorContext, lsp_kinds::Structure},
render_label,
- syntax::{latex, CstNode},
+ syntax::latex,
LabelledObject,
};
@@ -14,18 +13,13 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_labels<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (range, is_math) = find_reference(context).or_else(|| find_reference_range(context))?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- for label in data
- .root
+ for label in latex::SyntaxNode::new_root(data.green.clone())
.descendants()
- .filter(|_| !cancellation_token.is_canceled())
.filter_map(latex::LabelDefinition::cast)
{
if let Some(name) = label
@@ -33,7 +27,7 @@ pub fn complete_labels<'a>(
.and_then(|name| name.key())
.map(|name| name.to_string())
{
- match render_label(&context.request.subset, &name, Some(label)) {
+ match render_label(&context.request.workspace, &name, Some(label)) {
Some(rendered_label) => {
let kind = match &rendered_label.object {
LabelledObject::Section { .. } => Structure::Section,
@@ -108,7 +102,7 @@ fn find_reference_range(context: &CursorContext<CompletionParams>) -> Option<(Te
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -126,7 +120,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_labels(&context, &mut actual_items, CancellationToken::none());
+ complete_labels(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -143,7 +137,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_labels(&context, &mut actual_items, CancellationToken::none());
+ complete_labels(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -160,7 +154,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_labels(&context, &mut actual_items, CancellationToken::none());
+ complete_labels(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -180,7 +174,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_labels(&context, &mut actual_items, CancellationToken::none());
+ complete_labels(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -200,7 +194,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_labels(&context, &mut actual_items, CancellationToken::none());
+ complete_labels(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/theorem.rs b/support/texlab/src/features/completion/theorem.rs
index 25eea87ab7..0945d1df51 100644
--- a/support/texlab/src/features/completion/theorem.rs
+++ b/support/texlab/src/features/completion/theorem.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::features::cursor::CursorContext;
@@ -8,21 +7,16 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_theorem_environments<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- token: &CancellationToken,
) -> Option<()> {
- if token.is_canceled() {
- return None;
- }
-
let (_, range) = context.find_environment_name()?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
for environment in &data.extras.theorem_environments {
items.push(InternalCompletionItem::new(
range,
InternalCompletionItemData::UserEnvironment {
- name: &environment.name,
+ name: environment.name.clone(),
},
));
}
@@ -34,7 +28,7 @@ pub fn complete_theorem_environments<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -52,7 +46,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_theorem_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_theorem_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -69,7 +63,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_theorem_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_theorem_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -86,7 +80,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_theorem_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_theorem_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -109,7 +103,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_theorem_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_theorem_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -129,7 +123,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_theorem_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_theorem_environments(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/tikz_library.rs b/support/texlab/src/features/completion/tikz_library.rs
index b9c5e3ec7a..62d66782a3 100644
--- a/support/texlab/src/features/completion/tikz_library.rs
+++ b/support/texlab/src/features/completion/tikz_library.rs
@@ -1,21 +1,14 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
- LANGUAGE_DATA,
-};
+use crate::{features::cursor::CursorContext, syntax::latex, LANGUAGE_DATA};
use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_tikz_libraries<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (_, range, group) = context.find_curly_group_word_list()?;
let import = latex::TikzLibraryImport::cast(group.syntax().parent()?)?;
@@ -41,7 +34,7 @@ pub fn complete_tikz_libraries<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -59,7 +52,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -76,7 +69,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -93,7 +86,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -113,7 +106,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -133,7 +126,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
@@ -153,7 +146,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_tikz_libraries(&context, &mut actual_items, CancellationToken::none());
+ complete_tikz_libraries(&context, &mut actual_items);
assert!(!actual_items.is_empty());
for item in actual_items {
diff --git a/support/texlab/src/features/completion/types.rs b/support/texlab/src/features/completion/types.rs
index b7e5c09592..3655c6a074 100644
--- a/support/texlab/src/features/completion/types.rs
+++ b/support/texlab/src/features/completion/types.rs
@@ -1,10 +1,11 @@
use std::sync::Arc;
-use cstree::TextRange;
+use lsp_types::Url;
+use rowan::TextRange;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
-use crate::{features::lsp_kinds::Structure, BibtexEntryTypeDoc, BibtexFieldDoc, Uri};
+use crate::{features::lsp_kinds::Structure, BibtexEntryTypeDoc, BibtexFieldDoc};
#[derive(Debug, Clone)]
pub struct InternalCompletionItem<'a> {
@@ -39,7 +40,7 @@ pub enum InternalCompletionItemData<'a> {
},
BeginCommand,
Citation {
- uri: Arc<Uri>,
+ uri: Arc<Url>,
key: String,
text: String,
ty: Structure,
@@ -86,10 +87,10 @@ pub enum InternalCompletionItemData<'a> {
text: String,
},
UserCommand {
- name: &'a str,
+ name: SmolStr,
},
UserEnvironment {
- name: &'a str,
+ name: String,
},
PgfLibrary {
name: &'a str,
@@ -106,7 +107,7 @@ impl<'a> InternalCompletionItemData<'a> {
Self::Field { field } => &field.name,
Self::Argument { name, .. } => name,
Self::BeginCommand => "begin",
- Self::Citation { key, .. } => &key,
+ Self::Citation { key, .. } => key,
Self::ComponentCommand { name, .. } => name,
Self::ComponentEnvironment { name, .. } => name,
Self::Class { name } => name,
@@ -143,7 +144,7 @@ pub enum CompletionItemData {
Class,
EntryType,
FieldName,
- Citation { uri: Uri, key: SmolStr },
+ Citation { uri: Url, key: SmolStr },
Argument,
Acronym,
GlossaryEntry,
diff --git a/support/texlab/src/features/completion/user_command.rs b/support/texlab/src/features/completion/user_command.rs
index 8b4473f31c..8355025098 100644
--- a/support/texlab/src/features/completion/user_command.rs
+++ b/support/texlab/src/features/completion/user_command.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::features::cursor::CursorContext;
@@ -8,23 +7,19 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_user_commands<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let range = context.cursor.command_range(context.offset)?;
let token = context.cursor.as_latex()?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
for name in data
.extras
.command_names
.iter()
.filter(|name| name.as_str() != token.text())
- .map(|name| &name[1..])
+ .cloned()
{
- cancellation_token.result().ok()?;
items.push(InternalCompletionItem::new(
range,
InternalCompletionItemData::UserCommand { name },
@@ -38,7 +33,7 @@ pub fn complete_user_commands<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -56,7 +51,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_user_commands(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -73,7 +68,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_user_commands(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -90,7 +85,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_commands(&context, &mut actual_items, CancellationToken::none());
+ complete_user_commands(&context, &mut actual_items);
assert_eq!(actual_items.len(), 1);
for item in actual_items {
diff --git a/support/texlab/src/features/completion/user_environment.rs b/support/texlab/src/features/completion/user_environment.rs
index 5529143adc..3db135865e 100644
--- a/support/texlab/src/features/completion/user_environment.rs
+++ b/support/texlab/src/features/completion/user_environment.rs
@@ -1,4 +1,3 @@
-use cancellation::CancellationToken;
use lsp_types::CompletionParams;
use crate::features::cursor::CursorContext;
@@ -8,21 +7,18 @@ use super::types::{InternalCompletionItem, InternalCompletionItemData};
pub fn complete_user_environments<'a>(
context: &'a CursorContext<CompletionParams>,
items: &mut Vec<InternalCompletionItem<'a>>,
- cancellation_token: &CancellationToken,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (name, range) = context.find_environment_name()?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
for name in data
.extras
.environment_names
.iter()
.filter(|n| n.as_str() != name)
+ .cloned()
{
- cancellation_token.result().ok()?;
items.push(InternalCompletionItem::new(
range,
InternalCompletionItemData::UserEnvironment { name },
@@ -36,7 +32,7 @@ pub fn complete_user_environments<'a>(
#[cfg(test)]
mod tests {
- use cstree::TextRange;
+ use rowan::TextRange;
use crate::features::testing::FeatureTester;
@@ -54,7 +50,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_user_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -71,7 +67,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_user_environments(&context, &mut actual_items);
assert!(actual_items.is_empty());
}
@@ -91,7 +87,7 @@ mod tests {
let context = CursorContext::new(request);
let mut actual_items = Vec::new();
- complete_user_environments(&context, &mut actual_items, CancellationToken::none());
+ complete_user_environments(&context, &mut actual_items);
assert_eq!(actual_items.len(), 1);
for item in actual_items {
diff --git a/support/texlab/src/features/completion/util.rs b/support/texlab/src/features/completion/util.rs
index 5c4d83e4e9..05827bbf24 100644
--- a/support/texlab/src/features/completion/util.rs
+++ b/support/texlab/src/features/completion/util.rs
@@ -31,10 +31,9 @@ pub fn image_documentation(
fn supports_images(request: &FeatureRequest<CompletionParams>) -> bool {
request
- .context
+ .workspace
+ .environment
.client_capabilities
- .lock()
- .unwrap()
.text_document
.as_ref()
.and_then(|cap| cap.completion.as_ref())
@@ -48,10 +47,9 @@ pub fn adjust_kind(
kind: CompletionItemKind,
) -> CompletionItemKind {
if let Some(value_set) = request
- .context
+ .workspace
+ .environment
.client_capabilities
- .lock()
- .unwrap()
.text_document
.as_ref()
.and_then(|cap| cap.completion.as_ref())
diff --git a/support/texlab/src/features/cursor.rs b/support/texlab/src/features/cursor.rs
index d1de067091..84ee6e98c4 100644
--- a/support/texlab/src/features/cursor.rs
+++ b/support/texlab/src/features/cursor.rs
@@ -1,11 +1,11 @@
-use cstree::{TextRange, TextSize};
use lsp_types::{
CompletionParams, DocumentHighlightParams, GotoDefinitionParams, HoverParams, Position,
ReferenceParams, RenameParams, TextDocumentPositionParams,
};
+use rowan::{ast::AstNode, TextRange, TextSize};
use crate::{
- syntax::{bibtex, latex, CstNode},
+ syntax::{bibtex, latex},
DocumentData, LineIndexExt,
};
@@ -42,12 +42,12 @@ impl Cursor {
return Some(Self::Latex(right));
}
- if left.kind() == latex::WHITESPACE && left.parent().kind() == latex::KEY {
+ if left.kind() == latex::WHITESPACE && left.parent()?.kind() == latex::KEY {
return Some(Self::Latex(left));
}
if matches!(right.kind(), latex::WHITESPACE | latex::LINE_BREAK)
- && right.parent().kind() == latex::KEY
+ && right.parent()?.kind() == latex::KEY
{
return Some(Self::Latex(right));
}
@@ -62,27 +62,27 @@ impl Cursor {
let left = left?;
let right = right?;
- if right.kind().is_type() {
+ if right.kind() == bibtex::TYPE {
return Some(Self::Bibtex(right));
}
- if left.kind().is_type() {
+ if left.kind() == bibtex::TYPE {
return Some(Self::Bibtex(left));
}
- if left.kind() == bibtex::COMMAND_NAME {
+ if matches!(left.kind(), bibtex::COMMAND_NAME | bibtex::ACCENT_NAME) {
return Some(Self::Bibtex(left));
}
- if right.kind() == bibtex::WORD {
+ if matches!(right.kind(), bibtex::WORD | bibtex::NAME) {
return Some(Self::Bibtex(right));
}
- if left.kind() == bibtex::WORD {
+ if matches!(left.kind(), bibtex::WORD | bibtex::NAME) {
return Some(Self::Bibtex(left));
}
- if right.kind() == bibtex::COMMAND_NAME {
+ if matches!(right.kind(), bibtex::COMMAND_NAME | bibtex::ACCENT_NAME) {
return Some(Self::Bibtex(right));
}
@@ -113,7 +113,9 @@ impl Cursor {
.map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))
.or_else(|| {
self.as_bibtex()
- .filter(|token| token.kind() == bibtex::COMMAND_NAME)
+ .filter(|token| {
+ matches!(token.kind(), bibtex::COMMAND_NAME | bibtex::ACCENT_NAME)
+ })
.filter(|token| token.text_range().start() != offset)
.map(|token| token.text_range())
.map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))
@@ -136,13 +138,15 @@ impl<P: HasPosition> CursorContext<P> {
let cursor = match &main_document.data {
DocumentData::Latex(data) => {
- let left = data.root.token_at_offset(offset).left_biased();
- let right = data.root.token_at_offset(offset).right_biased();
+ let root = latex::SyntaxNode::new_root(data.green.clone());
+ let left = root.token_at_offset(offset).left_biased();
+ let right = root.token_at_offset(offset).right_biased();
Cursor::new_latex(left, right)
}
DocumentData::Bibtex(data) => {
- let left = data.root.token_at_offset(offset).left_biased();
- let right = data.root.token_at_offset(offset).right_biased();
+ let root = bibtex::SyntaxNode::new_root(data.green.clone());
+ let left = root.token_at_offset(offset).left_biased();
+ let right = root.token_at_offset(offset).right_biased();
Cursor::new_bibtex(left, right)
}
DocumentData::BuildLog(_) => None,
@@ -155,8 +159,8 @@ impl<P: HasPosition> CursorContext<P> {
}
}
- pub fn is_inside_latex_curly<'a>(&self, group: &impl latex::HasCurly<'a>) -> bool {
- group.small_range().contains(self.offset) || group.right_curly().is_none()
+ pub fn is_inside_latex_curly(&self, group: &impl latex::HasCurly) -> bool {
+ latex::small_range(group).contains(self.offset) || group.right_curly().is_none()
}
pub fn find_citation_key_word(&self) -> Option<(String, TextRange)> {
@@ -165,31 +169,29 @@ impl<P: HasPosition> CursorContext<P> {
.as_latex()
.filter(|token| token.kind() == latex::WORD)?;
- let key = latex::Key::cast(word.parent())?;
+ let key = latex::Key::cast(word.parent()?)?;
let group = latex::CurlyGroupWordList::cast(key.syntax().parent()?)?;
latex::Citation::cast(group.syntax().parent()?)?;
- Some((key.to_string(), key.small_range()))
+ Some((key.to_string(), latex::small_range(&key)))
}
pub fn find_citation_key_command(&self) -> Option<(String, TextRange)> {
let command = self.cursor.as_latex()?;
- let citation = latex::Citation::cast(command.parent())?;
+ let citation = latex::Citation::cast(command.parent()?)?;
let key = citation.key_list()?.keys().next()?;
- Some((key.to_string(), key.small_range()))
+ Some((key.to_string(), latex::small_range(&key)))
}
pub fn find_entry_key(&self) -> Option<(String, TextRange)> {
- let word = self
+ let key = self
.cursor
.as_bibtex()
- .filter(|token| token.kind() == bibtex::WORD)?;
-
- let key = bibtex::Key::cast(word.parent())?;
+ .filter(|token| token.kind() == bibtex::NAME)?;
- bibtex::Entry::cast(key.syntax().parent()?)?;
- Some((key.to_string(), key.small_range()))
+ bibtex::Entry::cast(key.parent()?)?;
+ Some((key.to_string(), key.text_range()))
}
pub fn find_label_name_key(&self) -> Option<(String, TextRange)> {
@@ -198,29 +200,29 @@ impl<P: HasPosition> CursorContext<P> {
.as_latex()
.filter(|token| token.kind() == latex::WORD)?;
- let key = latex::Key::cast(name.parent())?;
+ let key = latex::Key::cast(name.parent()?)?;
if matches!(
key.syntax().parent()?.parent()?.kind(),
latex::LABEL_DEFINITION | latex::LABEL_REFERENCE | latex::LABEL_REFERENCE_RANGE
) {
- Some((key.to_string(), key.small_range()))
+ Some((key.to_string(), latex::small_range(&key)))
} else {
None
}
}
pub fn find_label_name_command(&self) -> Option<(String, TextRange)> {
- let node = self.cursor.as_latex()?.parent();
- if let Some(label) = latex::LabelDefinition::cast(node) {
+ let node = self.cursor.as_latex()?.parent()?;
+ if let Some(label) = latex::LabelDefinition::cast(node.clone()) {
let name = label.name()?.key()?;
- Some((name.to_string(), name.small_range()))
- } else if let Some(label) = latex::LabelReference::cast(node) {
+ Some((name.to_string(), latex::small_range(&name)))
+ } else if let Some(label) = latex::LabelReference::cast(node.clone()) {
let name = label.name_list()?.keys().next()?;
- Some((name.to_string(), name.small_range()))
+ Some((name.to_string(), latex::small_range(&name)))
} else if let Some(label) = latex::LabelReferenceRange::cast(node) {
let name = label.from()?.key()?;
- Some((name.to_string(), name.small_range()))
+ Some((name.to_string(), latex::small_range(&name)))
} else {
None
}
@@ -238,17 +240,17 @@ impl<P: HasPosition> CursorContext<P> {
pub fn find_curly_group_word(&self) -> Option<(String, TextRange, latex::CurlyGroupWord)> {
let token = self.cursor.as_latex()?;
- let key = latex::Key::cast(token.parent());
+ let key = latex::Key::cast(token.parent()?);
let group = key
.as_ref()
.and_then(|key| key.syntax().parent())
- .unwrap_or(token.parent());
+ .unwrap_or(token.parent()?);
let group =
latex::CurlyGroupWord::cast(group).filter(|group| self.is_inside_latex_curly(group))?;
- key.map(|key| (key.to_string(), key.small_range(), group))
+ key.map(|key| (key.to_string(), latex::small_range(&key), group.clone()))
.or_else(|| Some((String::new(), TextRange::empty(self.offset), group)))
}
@@ -256,12 +258,12 @@ impl<P: HasPosition> CursorContext<P> {
&self,
) -> Option<(String, TextRange, latex::CurlyGroupWordList)> {
let token = self.cursor.as_latex()?;
- let key = latex::Key::cast(token.parent());
+ let key = latex::Key::cast(token.parent()?);
let group = key
.as_ref()
.and_then(|key| key.syntax().parent())
- .unwrap_or(token.parent());
+ .unwrap_or(token.parent()?);
let group = latex::CurlyGroupWordList::cast(group)
.filter(|group| self.is_inside_latex_curly(group))?;
@@ -273,12 +275,12 @@ impl<P: HasPosition> CursorContext<P> {
.filter(|tok| tok.kind() == latex::MISSING)
.is_some()
{
- TextRange::new(key.small_range().start(), token.text_range().end())
+ TextRange::new(latex::small_range(&key).start(), token.text_range().end())
} else {
- key.small_range()
+ latex::small_range(&key)
};
- (key.to_string(), range, group)
+ (key.to_string(), range, group.clone())
})
.or_else(|| Some((String::new(), TextRange::empty(self.offset), group)))
}
diff --git a/support/texlab/src/features/definition.rs b/support/texlab/src/features/definition.rs
index c49f172399..b54a2be9f0 100644
--- a/support/texlab/src/features/definition.rs
+++ b/support/texlab/src/features/definition.rs
@@ -4,7 +4,6 @@ mod entry;
mod label;
mod string;
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse};
use self::{
@@ -16,14 +15,13 @@ use super::{cursor::CursorContext, FeatureRequest};
pub fn goto_definition(
request: FeatureRequest<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<GotoDefinitionResponse> {
let context = CursorContext::new(request);
log::debug!("[Definition] Cursor: {:?}", context.cursor);
- let links = goto_command_definition(&context, cancellation_token)
- .or_else(|| goto_document_definition(&context, cancellation_token))
- .or_else(|| goto_entry_definition(&context, cancellation_token))
- .or_else(|| goto_label_definition(&context, cancellation_token))
- .or_else(|| goto_string_definition(&context, cancellation_token))?;
+ let links = goto_command_definition(&context)
+ .or_else(|| goto_document_definition(&context))
+ .or_else(|| goto_entry_definition(&context))
+ .or_else(|| goto_label_definition(&context))
+ .or_else(|| goto_string_definition(&context))?;
Some(GotoDefinitionResponse::Link(links))
}
diff --git a/support/texlab/src/features/definition/command.rs b/support/texlab/src/features/definition/command.rs
index a69796c33f..72cd4b5909 100644
--- a/support/texlab/src/features/definition/command.rs
+++ b/support/texlab/src/features/definition/command.rs
@@ -1,15 +1,10 @@
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, LocationLink};
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
- LineIndexExt,
-};
+use crate::{features::cursor::CursorContext, syntax::latex, LineIndexExt};
pub fn goto_command_definition(
context: &CursorContext<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<LocationLink>> {
let main_document = context.request.main_document();
@@ -22,16 +17,13 @@ pub fn goto_command_definition(
.line_index
.line_col_lsp_range(name.text_range());
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- for node in data.root.descendants() {
- cancellation_token.result().ok()?;
-
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
if let Some(defintion) = latex::CommandDefinition::cast(node).filter(|def| {
def.name()
.and_then(|name| name.command())
- .map(|name| name.text())
- == Some(name.text())
+ .map_or(false, |node| node.text() == name.text())
}) {
let target_selection_range = document
.line_index
@@ -39,11 +31,11 @@ pub fn goto_command_definition(
let target_range = document
.line_index
- .line_col_lsp_range(defintion.small_range());
+ .line_col_lsp_range(latex::small_range(&defintion));
return Some(vec![LocationLink {
origin_selection_range: Some(origin_selection_range),
- target_uri: document.uri.as_ref().clone().into(),
+ target_uri: document.uri.as_ref().clone(),
target_range,
target_selection_range,
}]);
@@ -76,7 +68,7 @@ mod tests {
let context = CursorContext::new(request);
- let actual_links = goto_command_definition(&context, CancellationToken::none());
+ let actual_links = goto_command_definition(&context);
assert!(actual_links.is_none());
}
@@ -92,7 +84,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_command_definition(&context, CancellationToken::none());
+ let actual_links = goto_command_definition(&context);
assert!(actual_links.is_none());
}
@@ -113,11 +105,11 @@ mod tests {
.line(1)
.character(2)
.build();
- let target_uri = tester.uri("main.tex").as_ref().clone().into();
+ let target_uri = tester.uri("main.tex").as_ref().clone();
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_command_definition(&context, CancellationToken::none()).unwrap();
+ let actual_links = goto_command_definition(&context).unwrap();
let expected_links = vec![LocationLink {
origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)),
diff --git a/support/texlab/src/features/definition/document.rs b/support/texlab/src/features/definition/document.rs
index b2b713f260..2a93232cd7 100644
--- a/support/texlab/src/features/definition/document.rs
+++ b/support/texlab/src/features/definition/document.rs
@@ -1,11 +1,9 @@
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, LocationLink, Range};
use crate::{features::cursor::CursorContext, LineIndexExt, RangeExt};
pub fn goto_document_definition(
context: &CursorContext<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<LocationLink>> {
let main_document = context.request.main_document();
if let Some(data) = main_document.data.as_latex() {
@@ -16,13 +14,11 @@ pub fn goto_document_definition(
.filter(|link| link.stem_range.contains_inclusive(context.offset))
{
for target in &include.targets {
- cancellation_token.result().ok()?;
-
if context
.request
- .subset
- .documents
- .iter()
+ .workspace
+ .documents_by_uri
+ .values()
.any(|document| document.uri.as_ref() == target.as_ref())
{
return Some(vec![LocationLink {
@@ -31,7 +27,7 @@ pub fn goto_document_definition(
.line_index
.line_col_lsp_range(include.stem_range),
),
- target_uri: target.as_ref().clone().into(),
+ target_uri: target.as_ref().clone(),
target_range: Range::new_simple(0, 0, 0, 0),
target_selection_range: Range::new_simple(0, 0, 0, 0),
}]);
@@ -61,7 +57,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_document_definition(&context, CancellationToken::none());
+ let actual_links = goto_document_definition(&context);
assert!(actual_links.is_none());
}
@@ -77,7 +73,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_document_definition(&context, CancellationToken::none());
+ let actual_links = goto_document_definition(&context);
assert!(actual_links.is_none());
}
@@ -94,11 +90,11 @@ mod tests {
.line(0)
.character(18)
.build();
- let target_uri = tester.uri("baz.bib").as_ref().clone().into();
+ let target_uri = tester.uri("baz.bib").as_ref().clone();
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_document_definition(&context, CancellationToken::none()).unwrap();
+ let actual_links = goto_document_definition(&context).unwrap();
let expected_links = vec![LocationLink {
origin_selection_range: Some(Range::new_simple(0, 16, 0, 23)),
diff --git a/support/texlab/src/features/definition/entry.rs b/support/texlab/src/features/definition/entry.rs
index b04490dc32..a1ae28705d 100644
--- a/support/texlab/src/features/definition/entry.rs
+++ b/support/texlab/src/features/definition/entry.rs
@@ -1,15 +1,17 @@
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, LocationLink};
+use rowan::ast::AstNode;
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, latex, CstNode},
+ syntax::{
+ bibtex::{self, HasName},
+ latex,
+ },
LineIndexExt,
};
pub fn goto_entry_definition(
context: &CursorContext<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<LocationLink>> {
let main_document = context.request.main_document();
@@ -18,27 +20,30 @@ pub fn goto_entry_definition(
.as_latex()
.filter(|token| token.kind() == latex::WORD)?;
- let key = latex::Key::cast(word.parent())?;
+ let key = latex::Key::cast(word.parent()?)?;
latex::Citation::cast(key.syntax().parent()?.parent()?)?;
let origin_selection_range = main_document
.line_index
- .line_col_lsp_range(key.small_range());
+ .line_col_lsp_range(latex::small_range(&key));
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_bibtex() {
- for entry in data.root.children().filter_map(bibtex::Entry::cast) {
- cancellation_token.result().ok()?;
-
- if let Some(key) = entry.key().filter(|k| k.to_string() == key.to_string()) {
+ for entry in bibtex::SyntaxNode::new_root(data.green.clone())
+ .children()
+ .filter_map(bibtex::Entry::cast)
+ {
+ if let Some(key) = entry.name_token().filter(|k| k.text() == word.text()) {
return Some(vec![LocationLink {
origin_selection_range: Some(origin_selection_range),
- target_uri: document.uri.as_ref().clone().into(),
+ target_uri: document.uri.as_ref().clone(),
target_selection_range: document
.line_index
- .line_col_lsp_range(key.small_range()),
- target_range: document.line_index.line_col_lsp_range(entry.small_range()),
+ .line_col_lsp_range(key.text_range()),
+ target_range: document
+ .line_index
+ .line_col_lsp_range(entry.syntax().text_range()),
}]);
}
}
@@ -68,7 +73,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_entry_definition(&context, CancellationToken::none());
+ let actual_links = goto_entry_definition(&context);
assert!(actual_links.is_none());
}
@@ -84,7 +89,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_entry_definition(&context, CancellationToken::none());
+ let actual_links = goto_entry_definition(&context);
assert!(actual_links.is_none());
}
@@ -109,11 +114,11 @@ mod tests {
.line(1)
.character(6)
.build();
- let target_uri = tester.uri("baz.bib").as_ref().clone().into();
+ let target_uri = tester.uri("baz.bib").as_ref().clone();
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_entry_definition(&context, CancellationToken::none()).unwrap();
+ let actual_links = goto_entry_definition(&context).unwrap();
let expected_links = vec![LocationLink {
origin_selection_range: Some(Range::new_simple(1, 6, 1, 9)),
diff --git a/support/texlab/src/features/definition/label.rs b/support/texlab/src/features/definition/label.rs
index fd30b1fbc5..e5ccd49937 100644
--- a/support/texlab/src/features/definition/label.rs
+++ b/support/texlab/src/features/definition/label.rs
@@ -1,14 +1,12 @@
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, LocationLink};
use crate::{
- features::cursor::CursorContext, find_label_definition, render_label, syntax::CstNode,
+ features::cursor::CursorContext, find_label_definition, render_label, syntax::latex,
LineIndexExt,
};
pub fn goto_label_definition(
context: &CursorContext<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<LocationLink>> {
let main_document = context.request.main_document();
@@ -18,19 +16,20 @@ pub fn goto_label_definition(
let origin_selection_range = main_document.line_index.line_col_lsp_range(name_range);
- for document in &context.request.subset.documents {
- cancellation_token.result().ok()?;
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- if let Some(definition) = find_label_definition(&data.root, &name_text) {
- let target_selection_range = definition.name()?.key()?.small_range();
+ if let Some(definition) =
+ find_label_definition(&latex::SyntaxNode::new_root(data.green.clone()), &name_text)
+ {
+ let target_selection_range = latex::small_range(&definition.name()?.key()?);
let target_range =
- render_label(&context.request.subset, &name_text, Some(definition))
+ render_label(&context.request.workspace, &name_text, Some(definition))
.map(|label| label.range)
.unwrap_or(target_selection_range);
return Some(vec![LocationLink {
origin_selection_range: Some(origin_selection_range),
- target_uri: document.uri.as_ref().clone().into(),
+ target_uri: document.uri.as_ref().clone(),
target_range: document.line_index.line_col_lsp_range(target_range),
target_selection_range: document
.line_index
@@ -60,7 +59,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_label_definition(&context, CancellationToken::none());
+ let actual_links = goto_label_definition(&context);
assert!(actual_links.is_none());
}
@@ -76,7 +75,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_label_definition(&context, CancellationToken::none());
+ let actual_links = goto_label_definition(&context);
assert!(actual_links.is_none());
}
diff --git a/support/texlab/src/features/definition/string.rs b/support/texlab/src/features/definition/string.rs
index 7162c8b722..09e00fdf92 100644
--- a/support/texlab/src/features/definition/string.rs
+++ b/support/texlab/src/features/definition/string.rs
@@ -1,43 +1,43 @@
-use cancellation::CancellationToken;
use lsp_types::{GotoDefinitionParams, LocationLink};
+use rowan::ast::AstNode;
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, CstNode},
+ syntax::bibtex::{self, HasName},
LineIndexExt,
};
pub fn goto_string_definition(
context: &CursorContext<GotoDefinitionParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<LocationLink>> {
let main_document = context.request.main_document();
let data = main_document.data.as_bibtex()?;
- let name = context
+ let key = context
.cursor
.as_bibtex()
- .filter(|token| token.kind() == bibtex::WORD)?;
+ .filter(|token| token.kind() == bibtex::NAME)?;
- bibtex::Token::cast(name.parent())?;
+ bibtex::Value::cast(key.parent()?)?;
let origin_selection_range = main_document
.line_index
- .line_col_lsp_range(name.text_range());
+ .line_col_lsp_range(key.text_range());
- for string in data.root.children().filter_map(bibtex::String::cast) {
- cancellation_token.result().ok()?;
-
- if let Some(string_name) = string.name().filter(|n| n.text() == name.text()) {
+ for string in bibtex::SyntaxNode::new_root(data.green.clone())
+ .children()
+ .filter_map(bibtex::StringDef::cast)
+ {
+ if let Some(string_name) = string.name_token().filter(|k| k.text() == key.text()) {
return Some(vec![LocationLink {
origin_selection_range: Some(origin_selection_range),
- target_uri: main_document.uri.as_ref().clone().into(),
+ target_uri: main_document.uri.as_ref().clone(),
target_selection_range: main_document
.line_index
.line_col_lsp_range(string_name.text_range()),
target_range: main_document
.line_index
- .line_col_lsp_range(string.small_range()),
+ .line_col_lsp_range(string.syntax().text_range()),
}]);
}
}
@@ -65,7 +65,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_string_definition(&context, CancellationToken::none());
+ let actual_links = goto_string_definition(&context);
assert!(actual_links.is_none());
}
@@ -81,7 +81,7 @@ mod tests {
.definition();
let context = CursorContext::new(request);
- let actual_links = goto_string_definition(&context, CancellationToken::none());
+ let actual_links = goto_string_definition(&context);
assert!(actual_links.is_none());
}
@@ -102,11 +102,11 @@ mod tests {
.line(1)
.character(24)
.build();
- let target_uri = tester.uri("main.bib").as_ref().clone().into();
+ let target_uri = tester.uri("main.bib").as_ref().clone();
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_string_definition(&context, CancellationToken::none()).unwrap();
+ let actual_links = goto_string_definition(&context).unwrap();
let expected_links = vec![LocationLink {
origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
@@ -119,7 +119,7 @@ mod tests {
}
#[test]
- fn concat() {
+ fn test_concat() {
let tester = FeatureTester::builder()
.files(vec![(
"main.bib",
@@ -134,11 +134,11 @@ mod tests {
.line(1)
.character(24)
.build();
- let target_uri = tester.uri("main.bib").as_ref().clone().into();
+ let target_uri = tester.uri("main.bib").as_ref().clone();
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_string_definition(&context, CancellationToken::none()).unwrap();
+ let actual_links = goto_string_definition(&context).unwrap();
let expected_links = vec![LocationLink {
origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
@@ -169,7 +169,7 @@ mod tests {
let request = tester.definition();
let context = CursorContext::new(request);
- let actual_links = goto_string_definition(&context, CancellationToken::none());
+ let actual_links = goto_string_definition(&context);
assert!(actual_links.is_none());
}
diff --git a/support/texlab/src/features/folding.rs b/support/texlab/src/features/folding.rs
index 0c44b6aa61..cd1a48f1a8 100644
--- a/support/texlab/src/features/folding.rs
+++ b/support/texlab/src/features/folding.rs
@@ -1,30 +1,25 @@
-use cancellation::CancellationToken;
use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams, Range};
+use rowan::ast::AstNode;
use crate::{
- syntax::{bibtex, latex, CstNode},
+ syntax::{bibtex, latex},
DocumentData, LineIndexExt,
};
use super::FeatureRequest;
-pub fn find_foldings(
- request: FeatureRequest<FoldingRangeParams>,
- token: &CancellationToken,
-) -> Vec<FoldingRange> {
+pub fn find_foldings(request: FeatureRequest<FoldingRangeParams>) -> Vec<FoldingRange> {
let mut foldings = Vec::new();
let main_document = request.main_document();
match &main_document.data {
DocumentData::Latex(data) => {
- for node in data.root.descendants() {
- if token.is_canceled() {
- break;
- }
-
- if let Some(folding) = latex::Environment::cast(node)
- .map(|node| node.small_range())
- .or_else(|| latex::Section::cast(node).map(|node| node.small_range()))
- .or_else(|| latex::EnumItem::cast(node).map(|node| node.small_range()))
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if let Some(folding) = latex::Environment::cast(node.clone())
+ .map(|node| latex::small_range(&node))
+ .or_else(|| {
+ latex::Section::cast(node.clone()).map(|node| latex::small_range(&node))
+ })
+ .or_else(|| latex::EnumItem::cast(node).map(|node| latex::small_range(&node)))
.map(|node| main_document.line_index.line_col_lsp_range(node))
.map(create_range)
{
@@ -33,19 +28,16 @@ pub fn find_foldings(
}
}
DocumentData::Bibtex(data) => {
- for node in data.root.descendants() {
- if token.is_canceled() {
- break;
- }
-
- if let Some(folding) = bibtex::Preamble::cast(node)
- .map(|node| node.small_range())
- .or_else(|| bibtex::String::cast(node).map(|node| node.small_range()))
- .or_else(|| bibtex::Entry::cast(node).map(|node| node.small_range()))
- .map(|node| main_document.line_index.line_col_lsp_range(node))
- .map(create_range)
- {
- foldings.push(folding);
+ for node in bibtex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if matches!(
+ node.kind(),
+ bibtex::PREAMBLE | bibtex::STRING | bibtex::ENTRY
+ ) {
+ foldings.push(create_range(
+ main_document
+ .line_index
+ .line_col_lsp_range(node.text_range()),
+ ));
}
}
}
@@ -80,7 +72,7 @@ mod tests {
.build()
.folding();
- let actual_foldings = find_foldings(request, CancellationToken::none());
+ let actual_foldings = find_foldings(request);
assert!(actual_foldings.is_empty());
}
@@ -92,7 +84,7 @@ mod tests {
.build()
.folding();
- let actual_foldings = find_foldings(request, CancellationToken::none());
+ let actual_foldings = find_foldings(request);
assert!(actual_foldings.is_empty());
}
@@ -116,7 +108,7 @@ mod tests {
.build()
.folding();
- let mut actual_foldings = find_foldings(req, CancellationToken::none());
+ let mut actual_foldings = find_foldings(req);
actual_foldings.sort_by_key(|folding| (folding.start_line, folding.start_character));
assert_eq!(actual_foldings.len(), 5);
@@ -199,7 +191,7 @@ mod tests {
.build()
.folding();
- let mut actual_foldings = find_foldings(request, CancellationToken::none());
+ let mut actual_foldings = find_foldings(request);
actual_foldings.sort_by_key(|folding| (folding.start_line, folding.start_character));
assert_eq!(actual_foldings.len(), 3);
diff --git a/support/texlab/src/features/formatting.rs b/support/texlab/src/features/formatting.rs
index c7c2dc410a..1107e5fd38 100644
--- a/support/texlab/src/features/formatting.rs
+++ b/support/texlab/src/features/formatting.rs
@@ -1,7 +1,6 @@
mod bibtex_internal;
mod latexindent;
-use cancellation::CancellationToken;
use lsp_types::{DocumentFormattingParams, TextEdit};
use crate::{BibtexFormatter, LatexFormatter};
@@ -12,17 +11,16 @@ use super::FeatureRequest;
pub fn format_source_code(
request: FeatureRequest<DocumentFormattingParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<TextEdit>> {
let mut edits = None;
- if request.context.options.read().unwrap().bibtex_formatter == BibtexFormatter::Texlab {
- edits = edits.or_else(|| format_bibtex_internal(&request, cancellation_token));
+ if request.workspace.environment.options.bibtex_formatter == BibtexFormatter::Texlab {
+ edits = edits.or_else(|| format_bibtex_internal(&request));
}
- if request.context.options.read().unwrap().latex_formatter == LatexFormatter::Texlab {
+ if request.workspace.environment.options.latex_formatter == LatexFormatter::Texlab {
edits = edits.or_else(|| Some(vec![]));
}
- edits = edits.or_else(|| format_with_latexindent(&request, cancellation_token));
+ edits = edits.or_else(|| format_with_latexindent(&request));
edits
}
diff --git a/support/texlab/src/features/formatting/bibtex_internal.rs b/support/texlab/src/features/formatting/bibtex_internal.rs
index 641bee0cfe..58aaf0635d 100644
--- a/support/texlab/src/features/formatting/bibtex_internal.rs
+++ b/support/texlab/src/features/formatting/bibtex_internal.rs
@@ -1,19 +1,14 @@
-use cancellation::CancellationToken;
-use cstree::NodeOrToken;
use lsp_types::{DocumentFormattingParams, TextEdit};
+use rowan::{ast::AstNode, NodeOrToken};
use crate::{
features::FeatureRequest,
- syntax::{
- bibtex::{self, HasType},
- CstNode,
- },
+ syntax::bibtex::{self, HasName, HasType, HasValue},
LineIndex, LineIndexExt,
};
pub fn format_bibtex_internal(
request: &FeatureRequest<DocumentFormattingParams>,
- _cancellation_token: &CancellationToken,
) -> Option<Vec<TextEdit>> {
let mut indent = String::new();
if request.params.options.insert_spaces {
@@ -24,37 +19,34 @@ pub fn format_bibtex_internal(
indent.push('\t');
}
- let line_length = {
- request
- .context
- .options
- .read()
- .unwrap()
- .formatter_line_length
- .map(|value| {
- if value <= 0 {
- usize::MAX
- } else {
- value as usize
- }
- })
- .unwrap_or(80)
- };
+ let line_length = request
+ .workspace
+ .environment
+ .options
+ .formatter_line_length
+ .map(|value| {
+ if value <= 0 {
+ usize::MAX
+ } else {
+ value as usize
+ }
+ })
+ .unwrap_or(80);
let document = request.main_document();
let data = document.data.as_bibtex()?;
let mut edits = Vec::new();
- for node in data.root.children() {
- let range = if let Some(entry) = bibtex::Entry::cast(node) {
- entry.small_range()
- } else if let Some(string) = bibtex::String::cast(node) {
- string.small_range()
- } else if let Some(preamble) = bibtex::Preamble::cast(node) {
- preamble.small_range()
- } else {
- continue;
- };
+ for node in bibtex::SyntaxNode::new_root(data.green.clone())
+ .children()
+ .filter(|node| {
+ matches!(
+ node.kind(),
+ bibtex::PREAMBLE | bibtex::STRING | bibtex::ENTRY
+ )
+ })
+ {
+ let range = node.text_range();
let mut formatter = Formatter::new(
indent.clone(),
@@ -115,13 +107,13 @@ impl<'a> Formatter<'a> {
.count()
}
- fn visit_node(&mut self, parent: &bibtex::SyntaxNode) {
+ fn visit_node(&mut self, parent: bibtex::SyntaxNode) {
match parent.kind() {
bibtex::PREAMBLE => {
let preamble = bibtex::Preamble::cast(parent).unwrap();
- self.visit_token_lowercase(preamble.ty().unwrap());
+ self.visit_token_lowercase(&preamble.type_token().unwrap());
self.output.push('{');
- if preamble.syntax().arity() > 0 {
+ if preamble.syntax().children().next().is_some() {
self.align.push(self.base_align());
for node in preamble.syntax().children() {
self.visit_node(node);
@@ -130,29 +122,29 @@ impl<'a> Formatter<'a> {
}
}
bibtex::STRING => {
- let string = bibtex::String::cast(parent).unwrap();
- self.visit_token_lowercase(string.ty().unwrap());
+ let string = bibtex::StringDef::cast(parent).unwrap();
+ self.visit_token_lowercase(&string.type_token().unwrap());
self.output.push('{');
- if let Some(name) = string.name() {
+ if let Some(name) = string.name_token() {
self.output.push_str(name.text());
self.output.push_str(" = ");
if let Some(value) = string.value() {
self.align.push(self.base_align());
- self.visit_node(value.syntax());
+ self.visit_node(value.syntax().clone());
self.output.push('}');
}
}
}
bibtex::ENTRY => {
let entry = bibtex::Entry::cast(parent).unwrap();
- self.visit_token_lowercase(entry.ty().unwrap());
+ self.visit_token_lowercase(&entry.type_token().unwrap());
self.output.push('{');
- if let Some(key) = entry.key() {
+ if let Some(key) = entry.name_token() {
self.output.push_str(&key.to_string());
self.output.push(',');
self.output.push('\n');
for field in entry.fields() {
- self.visit_node(field.syntax());
+ self.visit_node(field.syntax().clone());
}
self.output.push('}');
}
@@ -160,18 +152,18 @@ impl<'a> Formatter<'a> {
bibtex::FIELD => {
let field = bibtex::Field::cast(parent).unwrap();
self.output.push_str(&self.indent);
- let name = field.name().unwrap();
+ let name = field.name_token().unwrap();
self.output.push_str(name.text());
self.output.push_str(" = ");
if let Some(value) = field.value() {
let count = name.text().chars().count();
self.align.push(self.tab_size as usize + count + 3);
- self.visit_node(value.syntax());
+ self.visit_node(value.syntax().clone());
self.output.push(',');
self.output.push('\n');
}
}
- bibtex::VALUE => {
+ kind if bibtex::Value::can_cast(kind) => {
let tokens: Vec<_> = parent
.descendants_with_tokens()
.filter_map(|element| element.into_token())
@@ -183,8 +175,8 @@ impl<'a> Formatter<'a> {
let align = self.align.pop().unwrap_or_default();
let mut length = align + tokens[0].text().chars().count();
for i in 1..tokens.len() {
- let previous = tokens[i - 1];
- let current = tokens[i];
+ let previous = &tokens[i - 1];
+ let current = &tokens[i];
let current_length = current.text().chars().count();
let insert_space = self.should_insert_space(previous, current);
@@ -205,7 +197,7 @@ impl<'a> Formatter<'a> {
length += current_length;
}
}
- bibtex::ROOT | bibtex::JUNK | bibtex::COMMENT => {
+ bibtex::ROOT | bibtex::JUNK => {
for element in parent.children_with_tokens() {
match element {
NodeOrToken::Token(token) => {
@@ -241,10 +233,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -260,7 +249,7 @@ mod tests {
.build()
.formatting();
- let mut edits = format_bibtex_internal(&request, CancellationToken::none()).unwrap();
+ let mut edits = format_bibtex_internal(&request).unwrap();
let edit2 = edits.pop().unwrap();
let edit1 = edits.pop().unwrap();
@@ -275,10 +264,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -291,10 +277,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -307,10 +290,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -323,10 +303,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -339,10 +316,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -355,10 +329,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
@@ -371,10 +342,7 @@ mod tests {
.build()
.formatting();
- let edit = format_bibtex_internal(&request, CancellationToken::none())
- .unwrap()
- .pop()
- .unwrap();
+ let edit = format_bibtex_internal(&request).unwrap().pop().unwrap();
assert_display_snapshot!(edit.new_text);
}
diff --git a/support/texlab/src/features/formatting/latexindent.rs b/support/texlab/src/features/formatting/latexindent.rs
index 300f512578..4898aa4415 100644
--- a/support/texlab/src/features/formatting/latexindent.rs
+++ b/support/texlab/src/features/formatting/latexindent.rs
@@ -3,21 +3,19 @@ use std::{
process::{Command, Stdio},
};
-use cancellation::CancellationToken;
-use cstree::{TextLen, TextRange};
use lsp_types::{DocumentFormattingParams, TextEdit};
+use rowan::{TextLen, TextRange};
use tempfile::tempdir;
use crate::{features::FeatureRequest, DocumentLanguage, LineIndexExt};
pub fn format_with_latexindent(
request: &FeatureRequest<DocumentFormattingParams>,
- _cancellation_token: &CancellationToken,
) -> Option<Vec<TextEdit>> {
let directory = tempdir().ok()?;
let document = request.main_document();
- let options = request.context.options.read().unwrap();
+ let options = &request.workspace.environment.options;
let current_dir = options
.root_directory
.as_ref()
@@ -43,8 +41,6 @@ pub fn format_with_latexindent(
let modify_line_breaks = options.latexindent.modify_line_breaks;
- drop(options);
-
let path = directory.path();
let _ = fs::copy(
current_dir.join("localSettings.yaml"),
@@ -59,13 +55,13 @@ pub fn format_with_latexindent(
path.join("latexindent.yaml"),
);
- let name = if document.language() == DocumentLanguage::Bibtex {
+ let name = if document.data.language() == DocumentLanguage::Bibtex {
"file.bib"
} else {
"file.tex"
};
- fs::write(directory.path().join(name), &document.text).ok()?;
+ fs::write(directory.path().join(name), document.text.as_str()).ok()?;
let mut args = Vec::new();
if modify_line_breaks {
diff --git a/support/texlab/src/features/forward_search.rs b/support/texlab/src/features/forward_search.rs
index 4a54b8a52a..521cc31a59 100644
--- a/support/texlab/src/features/forward_search.rs
+++ b/support/texlab/src/features/forward_search.rs
@@ -4,7 +4,6 @@ use std::{
process::{Command, Stdio},
};
-use cancellation::CancellationToken;
use log::error;
use lsp_types::TextDocumentPositionParams;
use serde::{Deserialize, Serialize};
@@ -28,18 +27,8 @@ pub struct ForwardSearchResult {
pub fn execute_forward_search(
request: FeatureRequest<TextDocumentPositionParams>,
- _cancellation_token: &CancellationToken,
) -> Option<ForwardSearchResult> {
- let options = {
- request
- .context
- .options
- .read()
- .unwrap()
- .forward_search
- .clone()
- .unwrap_or_default()
- };
+ let options = &request.workspace.environment.options.forward_search;
if options.executable.is_none() || options.args.is_none() {
return Some(ForwardSearchResult {
@@ -48,9 +37,9 @@ pub fn execute_forward_search(
}
let root_document = request
- .subset
- .documents
- .iter()
+ .workspace
+ .documents_by_uri
+ .values()
.find(|document| {
if let Some(data) = document.data.as_latex() {
data.extras.has_document_environment
@@ -79,14 +68,15 @@ pub fn execute_forward_search(
let args: Vec<String> = options
.args
+ .as_ref()
.unwrap()
- .into_iter()
+ .iter()
.flat_map(|arg| {
replace_placeholder(&tex_path, &pdf_path, request.params.position.line, arg)
})
.collect();
- let status = match run_process(options.executable.unwrap(), args) {
+ let status = match run_process(options.executable.as_ref().unwrap(), args) {
Ok(()) => ForwardSearchStatus::SUCCESS,
Err(why) => {
error!("Unable to execute forward search: {}", why);
@@ -100,10 +90,10 @@ fn replace_placeholder(
tex_file: &Path,
pdf_file: &Path,
line_number: u32,
- argument: String,
+ argument: &str,
) -> Option<String> {
let result = if argument.starts_with('"') || argument.ends_with('"') {
- argument
+ argument.to_string()
} else {
argument
.replace("%f", tex_file.to_str()?)
@@ -113,7 +103,7 @@ fn replace_placeholder(
Some(result)
}
-fn run_process(executable: String, args: Vec<String>) -> io::Result<()> {
+fn run_process(executable: &str, args: Vec<String>) -> io::Result<()> {
Command::new(executable)
.args(args)
.stdin(Stdio::null())
diff --git a/support/texlab/src/features/highlight.rs b/support/texlab/src/features/highlight.rs
index 386018f353..b2c5794461 100644
--- a/support/texlab/src/features/highlight.rs
+++ b/support/texlab/src/features/highlight.rs
@@ -1,6 +1,5 @@
mod label;
-use cancellation::CancellationToken;
use lsp_types::{DocumentHighlight, DocumentHighlightParams};
use self::label::find_label_highlights;
@@ -9,8 +8,7 @@ use super::{cursor::CursorContext, FeatureRequest};
pub fn find_document_highlights(
request: FeatureRequest<DocumentHighlightParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<DocumentHighlight>> {
let context = CursorContext::new(request);
- find_label_highlights(&context, cancellation_token)
+ find_label_highlights(&context)
}
diff --git a/support/texlab/src/features/highlight/label.rs b/support/texlab/src/features/highlight/label.rs
index 9394378c09..3e8b95eac8 100644
--- a/support/texlab/src/features/highlight/label.rs
+++ b/support/texlab/src/features/highlight/label.rs
@@ -1,15 +1,10 @@
-use cancellation::CancellationToken;
use lsp_types::{DocumentHighlight, DocumentHighlightKind, DocumentHighlightParams};
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{latex, CstNode},
- LineIndexExt,
-};
+use crate::{features::cursor::CursorContext, syntax::latex, LineIndexExt};
pub fn find_label_highlights(
context: &CursorContext<DocumentHighlightParams>,
- cancellation_token: &CancellationToken,
) -> Option<Vec<DocumentHighlight>> {
let (name_text, _) = context.find_label_name_key()?;
@@ -17,23 +12,21 @@ pub fn find_label_highlights(
let data = main_document.data.as_latex()?;
let mut highlights = Vec::new();
- for node in data.root.descendants() {
- cancellation_token.result().ok()?;
-
- if let Some(label_name) = latex::LabelDefinition::cast(node)
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if let Some(label_name) = latex::LabelDefinition::cast(node.clone())
.and_then(|label| label.name())
.and_then(|label_name| label_name.key())
.filter(|label_name| label_name.to_string() == name_text)
{
let range = main_document
.line_index
- .line_col_lsp_range(label_name.small_range());
+ .line_col_lsp_range(latex::small_range(&label_name));
highlights.push(DocumentHighlight {
range,
kind: Some(DocumentHighlightKind::WRITE),
});
- } else if let Some(label) = latex::LabelReference::cast(node) {
+ } else if let Some(label) = latex::LabelReference::cast(node.clone()) {
for label_name in label
.name_list()
.into_iter()
@@ -42,14 +35,14 @@ pub fn find_label_highlights(
{
let range = main_document
.line_index
- .line_col_lsp_range(label_name.small_range());
+ .line_col_lsp_range(latex::small_range(&label_name));
highlights.push(DocumentHighlight {
range,
kind: Some(DocumentHighlightKind::READ),
});
}
- } else if let Some(label) = latex::LabelReferenceRange::cast(node) {
+ } else if let Some(label) = latex::LabelReferenceRange::cast(node.clone()) {
if let Some(label_name) = label
.from()
.and_then(|label_name| label_name.key())
@@ -57,7 +50,7 @@ pub fn find_label_highlights(
{
let range = main_document
.line_index
- .line_col_lsp_range(label_name.small_range());
+ .line_col_lsp_range(latex::small_range(&label_name));
highlights.push(DocumentHighlight {
range,
@@ -72,7 +65,7 @@ pub fn find_label_highlights(
{
let range = main_document
.line_index
- .line_col_lsp_range(label_name.small_range());
+ .line_col_lsp_range(latex::small_range(&label_name));
highlights.push(DocumentHighlight {
range,
@@ -104,7 +97,7 @@ mod tests {
.highlight();
let context = CursorContext::new(request);
- let actual_links = find_label_highlights(&context, CancellationToken::none());
+ let actual_links = find_label_highlights(&context);
assert!(actual_links.is_none());
}
@@ -120,7 +113,7 @@ mod tests {
.highlight();
let context = CursorContext::new(request);
- let actual_links = find_label_highlights(&context, CancellationToken::none());
+ let actual_links = find_label_highlights(&context);
assert!(actual_links.is_none());
}
@@ -136,7 +129,7 @@ mod tests {
let request = tester.highlight();
let context = CursorContext::new(request);
- let actual_highlights = find_label_highlights(&context, CancellationToken::none()).unwrap();
+ let actual_highlights = find_label_highlights(&context).unwrap();
let expected_highlights = vec![
DocumentHighlight {
diff --git a/support/texlab/src/features/hover.rs b/support/texlab/src/features/hover.rs
index efd7555a57..7d349de0a3 100644
--- a/support/texlab/src/features/hover.rs
+++ b/support/texlab/src/features/hover.rs
@@ -1,4 +1,3 @@
-#[cfg(feature = "citation")]
mod citation;
mod component;
mod entry_type;
@@ -6,11 +5,9 @@ mod field;
mod label;
mod string_ref;
-use cancellation::CancellationToken;
-use cfg_if::cfg_if;
use lsp_types::{Hover, HoverParams};
-use crate::features::cursor::CursorContext;
+use crate::features::{cursor::CursorContext, hover::citation::find_citation_hover};
use self::{
component::find_component_hover, entry_type::find_entry_type_hover, field::find_field_hover,
@@ -19,25 +16,13 @@ use self::{
use super::FeatureRequest;
-pub fn find_hover(
- request: FeatureRequest<HoverParams>,
- cabcellation_token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_hover(request: FeatureRequest<HoverParams>) -> Option<Hover> {
let context = CursorContext::new(request);
log::debug!("[Hover] Cursor: {:?}", context.cursor);
- let mut hover = find_label_hover(&context, cabcellation_token);
-
- cfg_if! {
- if #[cfg(feature = "citation")] {
- hover = hover.or_else(|| self::citation::find_citation_hover(&context, cabcellation_token));
- }
- }
-
- hover = hover
- .or_else(|| find_component_hover(&context, cabcellation_token))
- .or_else(|| find_string_reference_hover(&context, cabcellation_token))
- .or_else(|| find_field_hover(&context, cabcellation_token))
- .or_else(|| find_entry_type_hover(&context, cabcellation_token));
-
- hover
+ find_label_hover(&context)
+ .or_else(|| find_citation_hover(&context))
+ .or_else(|| find_component_hover(&context))
+ .or_else(|| find_string_reference_hover(&context))
+ .or_else(|| find_field_hover(&context))
+ .or_else(|| find_entry_type_hover(&context))
}
diff --git a/support/texlab/src/features/hover/citation.rs b/support/texlab/src/features/hover/citation.rs
index c097a922f7..4a0e3d2313 100644
--- a/support/texlab/src/features/hover/citation.rs
+++ b/support/texlab/src/features/hover/citation.rs
@@ -1,13 +1,9 @@
-use cancellation::CancellationToken;
-use lsp_types::{Hover, HoverContents, HoverParams};
+use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind};
+use rowan::ast::AstNode;
-use crate::{citation, features::cursor::CursorContext, LineIndexExt};
+use crate::{citation, features::cursor::CursorContext, syntax::bibtex, LineIndexExt};
-pub fn find_citation_hover(
- context: &CursorContext<HoverParams>,
- cancellation_token: &CancellationToken,
-) -> Option<Hover> {
- cancellation_token.result().ok()?;
+pub fn find_citation_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let main_document = context.request.main_document();
let (key_text, key_range) = context
@@ -17,14 +13,20 @@ pub fn find_citation_hover(
let contents = context
.request
- .subset
- .documents
- .iter()
+ .workspace
+ .documents_by_uri
+ .values()
.find_map(|document| {
- document
- .data
- .as_bibtex()
- .and_then(|data| citation::render_citation(&data.root, &key_text))
+ document.data.as_bibtex().and_then(|data| {
+ let root = bibtex::SyntaxNode::new_root(data.green.clone());
+ let root = bibtex::Root::cast(root)?;
+ let entry = root.find_entry(&key_text)?;
+
+ citation::render(&entry).map(|value| MarkupContent {
+ kind: MarkupKind::Markdown,
+ value,
+ })
+ })
})?;
Some(Hover {
@@ -35,9 +37,9 @@ pub fn find_citation_hover(
#[cfg(test)]
mod tests {
- use lsp_types::{MarkupContent, MarkupKind, Range};
+ use insta::assert_debug_snapshot;
- use crate::{features::testing::FeatureTester, RangeExt};
+ use crate::features::testing::FeatureTester;
use super::*;
@@ -52,7 +54,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_citation_hover(&context, CancellationToken::none());
+ let actual_hover = find_citation_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -68,7 +70,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_citation_hover(&context, CancellationToken::none());
+ let actual_hover = find_citation_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -90,16 +92,9 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_citation_hover(&context, CancellationToken::none()).unwrap();
-
- let expected_hover = Hover {
- contents: HoverContents::Markup(MarkupContent {
- kind: MarkupKind::Markdown,
- value: "Bar, Foo. (1337). *Baz Qux*.".into(),
- }),
- range: Some(Range::new_simple(1, 6, 1, 9)),
- };
- assert_eq!(actual_hover, expected_hover);
+ let actual_hover = find_citation_hover(&context).unwrap();
+
+ assert_debug_snapshot!(actual_hover);
}
#[test]
@@ -119,15 +114,8 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_citation_hover(&context, CancellationToken::none()).unwrap();
-
- let expected_hover = Hover {
- contents: HoverContents::Markup(MarkupContent {
- kind: MarkupKind::Markdown,
- value: "Bar, Foo. (1337). *Baz Qux*.".into(),
- }),
- range: Some(Range::new_simple(0, 9, 0, 12)),
- };
- assert_eq!(actual_hover, expected_hover);
+ let actual_hover = find_citation_hover(&context).unwrap();
+
+ assert_debug_snapshot!(actual_hover);
}
}
diff --git a/support/texlab/src/features/hover/component.rs b/support/texlab/src/features/hover/component.rs
index abfdb781f1..7b7e6c6e02 100644
--- a/support/texlab/src/features/hover/component.rs
+++ b/support/texlab/src/features/hover/component.rs
@@ -1,21 +1,13 @@
-use cancellation::CancellationToken;
use lsp_types::{Hover, HoverContents, HoverParams};
use crate::{
component_db::COMPONENT_DATABASE, features::cursor::CursorContext, syntax::latex, LineIndexExt,
};
-pub fn find_component_hover(
- context: &CursorContext<HoverParams>,
- token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_component_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let main_document = context.request.main_document();
let data = main_document.data.as_latex()?;
for link in &data.extras.explicit_links {
- if token.is_canceled() {
- break;
- }
-
if matches!(
link.kind,
latex::ExplicitLinkKind::Package | latex::ExplicitLinkKind::Class
@@ -50,7 +42,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_component_hover(&context, CancellationToken::none());
+ let actual_hover = find_component_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -66,7 +58,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_component_hover(&context, CancellationToken::none());
+ let actual_hover = find_component_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -82,7 +74,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_component_hover(&context, CancellationToken::none()).unwrap();
+ let actual_hover = find_component_hover(&context).unwrap();
assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 12, 0, 19));
}
@@ -98,7 +90,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_component_hover(&context, CancellationToken::none());
+ let actual_hover = find_component_hover(&context);
assert_eq!(actual_hover, None);
}
diff --git a/support/texlab/src/features/hover/entry_type.rs b/support/texlab/src/features/hover/entry_type.rs
index e480b2f835..ab36177ae8 100644
--- a/support/texlab/src/features/hover/entry_type.rs
+++ b/support/texlab/src/features/hover/entry_type.rs
@@ -1,16 +1,12 @@
-use cancellation::CancellationToken;
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent};
-use crate::{features::cursor::CursorContext, LineIndexExt, LANGUAGE_DATA};
+use crate::{features::cursor::CursorContext, syntax::bibtex, LineIndexExt, LANGUAGE_DATA};
-pub fn find_entry_type_hover(
- context: &CursorContext<HoverParams>,
- _token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_entry_type_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let name = context
.cursor
.as_bibtex()
- .filter(|token| token.kind().is_type())?;
+ .filter(|token| token.kind() == bibtex::TYPE)?;
let docs = LANGUAGE_DATA.entry_type_documentation(&name.text()[1..])?;
Some(Hover {
@@ -47,7 +43,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_entry_type_hover(&context, CancellationToken::none());
+ let actual_hover = find_entry_type_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -63,7 +59,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_entry_type_hover(&context, CancellationToken::none());
+ let actual_hover = find_entry_type_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -79,7 +75,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_entry_type_hover(&context, CancellationToken::none()).unwrap();
+ let actual_hover = find_entry_type_hover(&context).unwrap();
let expected_hover = Hover {
contents: HoverContents::Markup(MarkupContent {
@@ -105,7 +101,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_entry_type_hover(&context, CancellationToken::none());
+ let actual_hover = find_entry_type_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -121,7 +117,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_entry_type_hover(&context, CancellationToken::none());
+ let actual_hover = find_entry_type_hover(&context);
assert_eq!(actual_hover, None);
}
diff --git a/support/texlab/src/features/hover/field.rs b/support/texlab/src/features/hover/field.rs
index 54a8718d69..daa0c301cd 100644
--- a/support/texlab/src/features/hover/field.rs
+++ b/support/texlab/src/features/hover/field.rs
@@ -1,26 +1,19 @@
-use cancellation::CancellationToken;
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent};
+use rowan::ast::AstNode;
-use crate::{
- features::cursor::CursorContext,
- syntax::{bibtex, CstNode},
- LineIndexExt, LANGUAGE_DATA,
-};
+use crate::{features::cursor::CursorContext, syntax::bibtex, LineIndexExt, LANGUAGE_DATA};
-pub fn find_field_hover(
- context: &CursorContext<HoverParams>,
- _token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_field_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let main_document = context.request.main_document();
let name = context
.cursor
.as_bibtex()
- .filter(|token| token.kind() == bibtex::WORD)?;
+ .filter(|token| token.kind() == bibtex::NAME)?;
- bibtex::Field::cast(name.parent())?;
+ bibtex::Field::cast(name.parent()?)?;
- let docs = LANGUAGE_DATA.field_documentation(&name.text())?;
+ let docs = LANGUAGE_DATA.field_documentation(name.text())?;
Some(Hover {
contents: HoverContents::Markup(MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
@@ -53,7 +46,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_field_hover(&context, CancellationToken::none());
+ let actual_hover = find_field_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -69,7 +62,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_field_hover(&context, CancellationToken::none());
+ let actual_hover = find_field_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -85,7 +78,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_field_hover(&context, CancellationToken::none()).unwrap();
+ let actual_hover = find_field_hover(&context).unwrap();
let expected_hover = Hover {
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
@@ -107,7 +100,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_field_hover(&context, CancellationToken::none());
+ let actual_hover = find_field_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -122,7 +115,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_field_hover(&context, CancellationToken::none());
+ let actual_hover = find_field_hover(&context);
assert_eq!(actual_hover, None);
}
}
diff --git a/support/texlab/src/features/hover/label.rs b/support/texlab/src/features/hover/label.rs
index 26477feb7c..1018c187c5 100644
--- a/support/texlab/src/features/hover/label.rs
+++ b/support/texlab/src/features/hover/label.rs
@@ -1,19 +1,15 @@
-use cancellation::CancellationToken;
use lsp_types::{Hover, HoverContents, HoverParams};
use crate::{features::cursor::CursorContext, render_label, LineIndexExt};
-pub fn find_label_hover(
- context: &CursorContext<HoverParams>,
- _token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_label_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let main_document = context.request.main_document();
let (name_text, name_range) = context
.find_label_name_key()
.or_else(|| context.find_label_name_command())?;
- let label = render_label(&context.request.subset, &name_text, None)?;
+ let label = render_label(&context.request.workspace, &name_text, None)?;
Some(Hover {
range: Some(main_document.line_index.line_col_lsp_range(name_range)),
@@ -40,7 +36,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_label_hover(&context, CancellationToken::none());
+ let actual_hover = find_label_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -56,7 +52,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_label_hover(&context, CancellationToken::none());
+ let actual_hover = find_label_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -72,7 +68,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_label_hover(&context, CancellationToken::none()).unwrap();
+ let actual_hover = find_label_hover(&context).unwrap();
assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 20, 0, 27));
}
diff --git a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap
new file mode 100644
index 0000000000..2bca88bbff
--- /dev/null
+++ b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_cite.snap
@@ -0,0 +1,25 @@
+---
+source: src/features/hover/citation.rs
+assertion_line: 97
+expression: actual_hover
+---
+Hover {
+ contents: Markup(
+ MarkupContent {
+ kind: Markdown,
+ value: "F. Bar: \"Baz Qux\". (1337).",
+ },
+ ),
+ range: Some(
+ Range {
+ start: Position {
+ line: 1,
+ character: 6,
+ },
+ end: Position {
+ line: 1,
+ character: 9,
+ },
+ },
+ ),
+}
diff --git a/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap
new file mode 100644
index 0000000000..73ac4b7b69
--- /dev/null
+++ b/support/texlab/src/features/hover/snapshots/texlab__features__hover__citation__tests__inside_entry.snap
@@ -0,0 +1,25 @@
+---
+source: src/features/hover/citation.rs
+assertion_line: 119
+expression: actual_hover
+---
+Hover {
+ contents: Markup(
+ MarkupContent {
+ kind: Markdown,
+ value: "F. Bar: \"Baz Qux\". (1337).",
+ },
+ ),
+ range: Some(
+ Range {
+ start: Position {
+ line: 0,
+ character: 9,
+ },
+ end: Position {
+ line: 0,
+ character: 12,
+ },
+ },
+ ),
+}
diff --git a/support/texlab/src/features/hover/string_ref.rs b/support/texlab/src/features/hover/string_ref.rs
index f6e7f1c873..f1df08e8ab 100644
--- a/support/texlab/src/features/hover/string_ref.rs
+++ b/support/texlab/src/features/hover/string_ref.rs
@@ -1,37 +1,37 @@
-use cancellation::CancellationToken;
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind};
+use rowan::ast::AstNode;
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, CstNode},
+ syntax::bibtex::{self, HasName, HasValue},
LineIndexExt,
};
-pub fn find_string_reference_hover(
- context: &CursorContext<HoverParams>,
- cancellation_token: &CancellationToken,
-) -> Option<Hover> {
+pub fn find_string_reference_hover(context: &CursorContext<HoverParams>) -> Option<Hover> {
let main_document = context.request.main_document();
let data = main_document.data.as_bibtex()?;
- let name = context
- .cursor
- .as_bibtex()
- .filter(|token| token.kind() == bibtex::WORD)
- .filter(|name| matches!(name.parent().kind(), bibtex::TOKEN | bibtex::STRING))?;
-
- for string in data.root.children().filter_map(bibtex::String::cast) {
- if cancellation_token.is_canceled() {
- return None;
- }
-
- if string.name().filter(|n| n.text() == name.text()).is_some() {
+ let key = context.cursor.as_bibtex().filter(|token| {
+ let parent = token.parent().unwrap();
+ (token.kind() == bibtex::NAME && bibtex::Value::can_cast(parent.kind()))
+ || (token.kind() == bibtex::NAME && bibtex::StringDef::can_cast(parent.kind()))
+ })?;
+
+ for string in bibtex::SyntaxNode::new_root(data.green.clone())
+ .children()
+ .filter_map(bibtex::StringDef::cast)
+ {
+ if string
+ .name_token()
+ .filter(|k| k.text() == key.text())
+ .is_some()
+ {
let value = string.value()?.syntax().text().to_string();
return Some(Hover {
range: Some(
main_document
.line_index
- .line_col_lsp_range(name.text_range()),
+ .line_col_lsp_range(key.text_range()),
),
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::PlainText,
@@ -64,7 +64,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_string_reference_hover(&context, CancellationToken::none());
+ let actual_hover = find_string_reference_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -80,7 +80,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_string_reference_hover(&context, CancellationToken::none());
+ let actual_hover = find_string_reference_hover(&context);
assert_eq!(actual_hover, None);
}
@@ -103,8 +103,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover =
- find_string_reference_hover(&context, CancellationToken::none()).unwrap();
+ let actual_hover = find_string_reference_hover(&context).unwrap();
let expected_hover = Hover {
contents: HoverContents::Markup(MarkupContent {
@@ -135,7 +134,7 @@ mod tests {
.hover();
let context = CursorContext::new(request);
- let actual_hover = find_string_reference_hover(&context, CancellationToken::none());
+ let actual_hover = find_string_reference_hover(&context);
assert_eq!(actual_hover, None);
}
}
diff --git a/support/texlab/src/features/link.rs b/support/texlab/src/features/link.rs
index a6016c9f76..0a02f7819e 100644
--- a/support/texlab/src/features/link.rs
+++ b/support/texlab/src/features/link.rs
@@ -1,34 +1,26 @@
-use cancellation::CancellationToken;
use lsp_types::{DocumentLink, DocumentLinkParams};
use crate::LineIndexExt;
use super::FeatureRequest;
-pub fn find_document_links(
- request: FeatureRequest<DocumentLinkParams>,
- cancellation_token: &CancellationToken,
-) -> Vec<DocumentLink> {
+pub fn find_document_links(request: FeatureRequest<DocumentLinkParams>) -> Vec<DocumentLink> {
let mut links = Vec::new();
let main_document = request.main_document();
if let Some(data) = main_document.data.as_latex() {
for include in &data.extras.explicit_links {
for target in &include.targets {
- if cancellation_token.is_canceled() {
- return links;
- }
-
if request
- .subset
- .documents
- .iter()
+ .workspace
+ .documents_by_uri
+ .values()
.any(|document| document.uri.as_ref() == target.as_ref())
{
links.push(DocumentLink {
range: main_document
.line_index
.line_col_lsp_range(include.stem_range),
- target: Some(target.as_ref().clone().into()),
+ target: Some(target.as_ref().clone()),
tooltip: None,
data: None,
});
@@ -56,7 +48,7 @@ mod tests {
.build()
.link();
- let items = find_document_links(request, CancellationToken::none());
+ let items = find_document_links(request);
assert!(items.is_empty());
}
@@ -68,7 +60,7 @@ mod tests {
.build()
.link();
- let items = find_document_links(request, CancellationToken::none());
+ let items = find_document_links(request);
assert!(items.is_empty());
}
@@ -80,11 +72,11 @@ mod tests {
.build();
let target = tester.uri("bar.tex");
- let actual_items = find_document_links(tester.link(), CancellationToken::none());
+ let actual_items = find_document_links(tester.link());
let expected_items = vec![DocumentLink {
range: Range::new_simple(0, 7, 0, 14),
- target: Some(target.as_ref().clone().into()),
+ target: Some(target.as_ref().clone()),
tooltip: None,
data: None,
}];
@@ -102,11 +94,11 @@ mod tests {
.build();
let target = tester.uri("bar/baz.tex");
- let actual_items = find_document_links(tester.link(), CancellationToken::none());
+ let actual_items = find_document_links(tester.link());
let expected_items = vec![DocumentLink {
range: Range::new_simple(0, 14, 0, 17),
- target: Some(target.as_ref().clone().into()),
+ target: Some(target.as_ref().clone()),
tooltip: None,
data: None,
}];
diff --git a/support/texlab/src/features/reference.rs b/support/texlab/src/features/reference.rs
index 14cf3bc297..db48453b4e 100644
--- a/support/texlab/src/features/reference.rs
+++ b/support/texlab/src/features/reference.rs
@@ -2,7 +2,6 @@ mod entry;
mod label;
mod string;
-use cancellation::CancellationToken;
use lsp_types::{Location, ReferenceParams};
use self::{
@@ -11,15 +10,12 @@ use self::{
use super::{cursor::CursorContext, FeatureRequest};
-pub fn find_all_references(
- request: FeatureRequest<ReferenceParams>,
- cancellation_token: &CancellationToken,
-) -> Option<Vec<Location>> {
+pub fn find_all_references(request: FeatureRequest<ReferenceParams>) -> Option<Vec<Location>> {
let mut references = Vec::new();
let context = CursorContext::new(request);
log::debug!("[References] Cursor: {:?}", context.cursor);
- find_label_references(&context, cancellation_token, &mut references);
- find_entry_references(&context, cancellation_token, &mut references);
- find_string_references(&context, cancellation_token, &mut references);
+ find_label_references(&context, &mut references);
+ find_entry_references(&context, &mut references);
+ find_string_references(&context, &mut references);
Some(references)
}
diff --git a/support/texlab/src/features/reference/entry.rs b/support/texlab/src/features/reference/entry.rs
index a9e15ba5b6..f245b6eb8d 100644
--- a/support/texlab/src/features/reference/entry.rs
+++ b/support/texlab/src/features/reference/entry.rs
@@ -1,15 +1,17 @@
-use cancellation::CancellationToken;
use lsp_types::{Location, ReferenceParams};
+use rowan::ast::AstNode;
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, latex, CstNode},
+ syntax::{
+ bibtex::{self, HasName},
+ latex,
+ },
DocumentData, LineIndexExt,
};
pub fn find_entry_references(
context: &CursorContext<ReferenceParams>,
- cancellation_token: &CancellationToken,
references: &mut Vec<Location>,
) -> Option<()> {
let (key_text, _) = context
@@ -17,31 +19,33 @@ pub fn find_entry_references(
.or_else(|| context.find_citation_key_command())
.or_else(|| context.find_entry_key())?;
- for document in &context.request.subset.documents {
- cancellation_token.result().ok()?;
-
+ for document in context.request.workspace.documents_by_uri.values() {
match &document.data {
DocumentData::Latex(data) => {
- data.root
+ latex::SyntaxNode::new_root(data.green.clone())
.descendants()
.filter_map(latex::Citation::cast)
.filter_map(|citation| citation.key_list())
.flat_map(|keys| keys.keys())
.filter(|key| key.to_string() == key_text)
- .map(|key| document.line_index.line_col_lsp_range(key.small_range()))
+ .map(|key| {
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&key))
+ })
.for_each(|range| {
- references.push(Location::new(document.uri.as_ref().clone().into(), range));
+ references.push(Location::new(document.uri.as_ref().clone(), range));
});
}
DocumentData::Bibtex(data) if context.request.params.context.include_declaration => {
- data.root
+ bibtex::SyntaxNode::new_root(data.green.clone())
.children()
.filter_map(bibtex::Entry::cast)
- .filter_map(|entry| entry.key())
- .filter(|key| key.to_string() == key_text)
- .map(|key| document.line_index.line_col_lsp_range(key.small_range()))
+ .filter_map(|entry| entry.name_token())
+ .filter(|key| key.text() == key_text)
+ .map(|key| document.line_index.line_col_lsp_range(key.text_range()))
.for_each(|range| {
- references.push(Location::new(document.uri.as_ref().clone().into(), range));
+ references.push(Location::new(document.uri.as_ref().clone(), range));
});
}
DocumentData::Bibtex(_) | DocumentData::BuildLog(_) => {}
@@ -58,6 +62,14 @@ mod tests {
use super::*;
+ fn sort_references(actual_references: &mut [Location]) {
+ actual_references.sort_by(|a, b| {
+ a.uri
+ .cmp(&b.uri)
+ .then_with(|| a.range.start.cmp(&b.range.start))
+ });
+ }
+
#[test]
fn test_empty_latex_document() {
let request = FeatureTester::builder()
@@ -70,7 +82,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
@@ -87,7 +99,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
@@ -108,10 +120,11 @@ mod tests {
let request = tester.reference();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![Location::new(
- uri.as_ref().clone().into(),
+ uri.as_ref().clone(),
Range::new_simple(0, 6, 0, 9),
)];
assert_eq!(actual_references, expected_references);
@@ -129,17 +142,18 @@ mod tests {
.character(11)
.include_declaration(true)
.build();
- let uri1 = tester.uri("foo.bib");
- let uri2 = tester.uri("bar.tex");
+ let uri1 = tester.uri("bar.tex");
+ let uri2 = tester.uri("foo.bib");
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![
- Location::new(uri1.as_ref().clone().into(), Range::new_simple(0, 9, 0, 12)),
- Location::new(uri2.as_ref().clone().into(), Range::new_simple(0, 6, 0, 9)),
+ Location::new(uri1.as_ref().clone(), Range::new_simple(0, 6, 0, 9)),
+ Location::new(uri2.as_ref().clone(), Range::new_simple(0, 9, 0, 12)),
];
assert_eq!(actual_references, expected_references);
}
@@ -160,10 +174,11 @@ mod tests {
let request = tester.reference();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![Location::new(
- uri.as_ref().clone().into(),
+ uri.as_ref().clone(),
Range::new_simple(0, 6, 0, 9),
)];
assert_eq!(actual_references, expected_references);
@@ -187,11 +202,12 @@ mod tests {
let request = tester.reference();
let context = CursorContext::new(request);
- find_entry_references(&context, CancellationToken::none(), &mut actual_references);
+ find_entry_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![
- Location::new(uri2.as_ref().clone().into(), Range::new_simple(0, 6, 0, 9)),
- Location::new(uri1.as_ref().clone().into(), Range::new_simple(0, 9, 0, 12)),
+ Location::new(uri2.as_ref().clone(), Range::new_simple(0, 6, 0, 9)),
+ Location::new(uri1.as_ref().clone(), Range::new_simple(0, 9, 0, 12)),
];
assert_eq!(actual_references, expected_references);
}
diff --git a/support/texlab/src/features/reference/label.rs b/support/texlab/src/features/reference/label.rs
index 45c6b90137..2fc3efc398 100644
--- a/support/texlab/src/features/reference/label.rs
+++ b/support/texlab/src/features/reference/label.rs
@@ -1,22 +1,17 @@
-use cancellation::CancellationToken;
use lsp_types::{Location, ReferenceParams};
use crate::{features::cursor::CursorContext, LineIndexExt};
pub fn find_label_references(
context: &CursorContext<ReferenceParams>,
- cancellation_token: &CancellationToken,
references: &mut Vec<Location>,
) -> Option<()> {
- cancellation_token.result().ok()?;
-
let (name_text, _) = context
.find_label_name_key()
.or_else(|| context.find_label_name_command())?;
- for document in &context.request.subset.documents {
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- cancellation_token.result().ok()?;
for name in data
.extras
.label_names
@@ -27,7 +22,7 @@ pub fn find_label_references(
})
{
references.push(Location::new(
- document.uri.as_ref().clone().into(),
+ document.uri.as_ref().clone(),
document.line_index.line_col_lsp_range(name.range),
));
}
@@ -45,6 +40,14 @@ mod tests {
use super::*;
+ fn sort_references(actual_references: &mut [Location]) {
+ actual_references.sort_by(|a, b| {
+ a.uri
+ .cmp(&b.uri)
+ .then_with(|| a.range.start.cmp(&b.range.start))
+ });
+ }
+
#[test]
fn test_definition() {
let tester = FeatureTester::builder()
@@ -61,10 +64,11 @@ mod tests {
let request = tester.reference();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![Location::new(
- uri.as_ref().clone().into(),
+ uri.as_ref().clone(),
Range::new_simple(0, 5, 0, 8),
)];
assert_eq!(actual_references, expected_references);
@@ -82,17 +86,18 @@ mod tests {
.character(9)
.include_declaration(true)
.build();
- let uri1 = tester.uri("foo.tex");
- let uri2 = tester.uri("bar.tex");
+ let uri1 = tester.uri("bar.tex");
+ let uri2 = tester.uri("foo.tex");
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![
- Location::new(uri1.as_ref().clone().into(), Range::new_simple(0, 7, 0, 10)),
- Location::new(uri2.as_ref().clone().into(), Range::new_simple(0, 5, 0, 8)),
+ Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)),
+ Location::new(uri2.as_ref().clone(), Range::new_simple(0, 7, 0, 10)),
];
assert_eq!(actual_references, expected_references);
}
@@ -115,11 +120,12 @@ mod tests {
let request = tester.reference();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![
- Location::new(uri1.as_ref().clone().into(), Range::new_simple(0, 5, 0, 8)),
- Location::new(uri2.as_ref().clone().into(), Range::new_simple(0, 5, 0, 8)),
+ Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)),
+ Location::new(uri2.as_ref().clone(), Range::new_simple(0, 5, 0, 8)),
];
assert_eq!(actual_references, expected_references);
}
@@ -136,17 +142,18 @@ mod tests {
.character(7)
.include_declaration(true)
.build();
- let uri1 = tester.uri("foo.tex");
- let uri2 = tester.uri("bar.tex");
+ let uri1 = tester.uri("bar.tex");
+ let uri2 = tester.uri("foo.tex");
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
+ sort_references(&mut actual_references);
let expected_references = vec![
- Location::new(uri2.as_ref().clone().into(), Range::new_simple(0, 5, 0, 8)),
- Location::new(uri1.as_ref().clone().into(), Range::new_simple(0, 7, 0, 10)),
+ Location::new(uri1.as_ref().clone(), Range::new_simple(0, 5, 0, 8)),
+ Location::new(uri2.as_ref().clone(), Range::new_simple(0, 7, 0, 10)),
];
assert_eq!(actual_references, expected_references);
}
@@ -163,7 +170,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
@@ -180,7 +187,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_label_references(&context, CancellationToken::none(), &mut actual_references);
+ find_label_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
diff --git a/support/texlab/src/features/reference/string.rs b/support/texlab/src/features/reference/string.rs
index b7dd4e039a..c957244a53 100644
--- a/support/texlab/src/features/reference/string.rs
+++ b/support/texlab/src/features/reference/string.rs
@@ -1,41 +1,42 @@
-use cancellation::CancellationToken;
use lsp_types::{Location, ReferenceParams};
+use rowan::ast::AstNode;
use crate::{
features::cursor::CursorContext,
- syntax::{bibtex, CstNode},
+ syntax::bibtex::{self, HasName},
LineIndexExt,
};
pub fn find_string_references(
context: &CursorContext<ReferenceParams>,
- cancellation_token: &CancellationToken,
items: &mut Vec<Location>,
) -> Option<()> {
let name_text = context
.cursor
.as_bibtex()
- .filter(|token| token.kind() == bibtex::WORD)
- .filter(|token| matches!(token.parent().kind(), bibtex::TOKEN | bibtex::STRING))?
+ .filter(|token| {
+ let parent = token.parent().unwrap();
+ (token.kind() == bibtex::NAME && bibtex::Value::can_cast(parent.kind()))
+ || (token.kind() == bibtex::NAME && bibtex::StringDef::can_cast(parent.kind()))
+ })?
.text();
let document = context.request.main_document();
let data = document.data.as_bibtex()?;
- for node in data.root.descendants() {
- cancellation_token.result().ok()?;
- if let Some(name) = bibtex::String::cast(node)
- .and_then(|string| string.name())
+ for node in bibtex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if let Some(name) = bibtex::StringDef::cast(node.clone())
+ .and_then(|string| string.name_token())
.filter(|name| {
context.request.params.context.include_declaration && name.text() == name_text
})
.or_else(|| {
- bibtex::Token::cast(node)
+ bibtex::Value::cast(node)
.and_then(|token| token.syntax().first_token())
.filter(|name| name.text() == name_text)
})
{
items.push(Location::new(
- document.uri.as_ref().clone().into(),
+ document.uri.as_ref().clone(),
document.line_index.line_col_lsp_range(name.text_range()),
));
}
@@ -72,10 +73,10 @@ mod tests {
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
let expected_references = vec![Location::new(
- uri.as_ref().clone().into(),
+ uri.as_ref().clone(),
Range::new_simple(2, 23, 2, 26),
)];
assert_eq!(actual_references, expected_references);
@@ -102,11 +103,11 @@ mod tests {
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
let expected_references = vec![
- Location::new(uri.as_ref().clone().into(), Range::new_simple(0, 8, 0, 11)),
- Location::new(uri.as_ref().clone().into(), Range::new_simple(2, 23, 2, 26)),
+ Location::new(uri.as_ref().clone(), Range::new_simple(0, 8, 0, 11)),
+ Location::new(uri.as_ref().clone(), Range::new_simple(2, 23, 2, 26)),
];
assert_eq!(actual_references, expected_references);
}
@@ -131,10 +132,10 @@ mod tests {
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
let expected_references = vec![Location::new(
- uri.as_ref().clone().into(),
+ uri.as_ref().clone(),
Range::new_simple(2, 23, 2, 26),
)];
assert_eq!(actual_references, expected_references);
@@ -161,11 +162,11 @@ mod tests {
let mut actual_references = Vec::new();
let request = tester.reference();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
let expected_references = vec![
- Location::new(uri.as_ref().clone().into(), Range::new_simple(0, 8, 0, 11)),
- Location::new(uri.as_ref().clone().into(), Range::new_simple(2, 23, 2, 26)),
+ Location::new(uri.as_ref().clone(), Range::new_simple(0, 8, 0, 11)),
+ Location::new(uri.as_ref().clone(), Range::new_simple(2, 23, 2, 26)),
];
assert_eq!(actual_references, expected_references);
}
@@ -182,7 +183,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
@@ -199,7 +200,7 @@ mod tests {
let mut actual_references = Vec::new();
let context = CursorContext::new(request);
- find_string_references(&context, CancellationToken::none(), &mut actual_references);
+ find_string_references(&context, &mut actual_references);
assert!(actual_references.is_empty());
}
diff --git a/support/texlab/src/features/rename.rs b/support/texlab/src/features/rename.rs
index ba09fa268c..a9a6732d04 100644
--- a/support/texlab/src/features/rename.rs
+++ b/support/texlab/src/features/rename.rs
@@ -2,7 +2,6 @@ mod command;
mod entry;
mod label;
-use cancellation::CancellationToken;
use lsp_types::{Range, RenameParams, TextDocumentPositionParams, WorkspaceEdit};
use self::{
@@ -13,22 +12,16 @@ use self::{
use super::{cursor::CursorContext, FeatureRequest};
-pub fn prepare_rename_all(
- request: FeatureRequest<TextDocumentPositionParams>,
- cancellation_token: &CancellationToken,
-) -> Option<Range> {
+pub fn prepare_rename_all(request: FeatureRequest<TextDocumentPositionParams>) -> Option<Range> {
let context = CursorContext::new(request);
- prepare_entry_rename(&context, cancellation_token)
- .or_else(|| prepare_label_rename(&context, cancellation_token))
- .or_else(|| prepare_command_rename(&context, cancellation_token))
+ prepare_entry_rename(&context)
+ .or_else(|| prepare_label_rename(&context))
+ .or_else(|| prepare_command_rename(&context))
}
-pub fn rename_all(
- request: FeatureRequest<RenameParams>,
- cancellation_token: &CancellationToken,
-) -> Option<WorkspaceEdit> {
+pub fn rename_all(request: FeatureRequest<RenameParams>) -> Option<WorkspaceEdit> {
let context = CursorContext::new(request);
- rename_entry(&context, cancellation_token)
- .or_else(|| rename_label(&context, cancellation_token))
- .or_else(|| rename_command(&context, cancellation_token))
+ rename_entry(&context)
+ .or_else(|| rename_label(&context))
+ .or_else(|| rename_command(&context))
}
diff --git a/support/texlab/src/features/rename/command.rs b/support/texlab/src/features/rename/command.rs
index 402783f3ae..e0677f443e 100644
--- a/support/texlab/src/features/rename/command.rs
+++ b/support/texlab/src/features/rename/command.rs
@@ -1,18 +1,15 @@
use std::collections::HashMap;
-use cancellation::CancellationToken;
-use cstree::{TextRange, TextSize};
use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit};
+use rowan::{TextRange, TextSize};
use crate::{
features::cursor::{CursorContext, HasPosition},
+ syntax::latex,
LineIndexExt,
};
-pub fn prepare_command_rename<P: HasPosition>(
- context: &CursorContext<P>,
- _cancellation_token: &CancellationToken,
-) -> Option<Range> {
+pub fn prepare_command_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> {
Some(
context
.request
@@ -22,20 +19,13 @@ pub fn prepare_command_rename<P: HasPosition>(
)
}
-pub fn rename_command(
- context: &CursorContext<RenameParams>,
- cancellation_token: &CancellationToken,
-) -> Option<WorkspaceEdit> {
- cancellation_token.result().ok()?;
- prepare_command_rename(context, cancellation_token)?;
+pub fn rename_command(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> {
+ prepare_command_rename(context)?;
let name = context.cursor.as_latex()?.text();
let mut changes = HashMap::new();
- for document in &context.request.subset.documents {
- cancellation_token.result().ok()?;
-
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- let edits = data
- .root
+ let edits = latex::SyntaxNode::new_root(data.green.clone())
.descendants_with_tokens()
.filter_map(|element| element.into_token())
.filter(|token| token.kind().is_command_name() && token.text() == name)
@@ -49,7 +39,7 @@ pub fn rename_command(
})
.collect();
- changes.insert(document.uri.as_ref().clone().into(), edits);
+ changes.insert(document.uri.as_ref().clone(), edits);
}
}
@@ -80,15 +70,15 @@ mod tests {
let req = tester.rename();
let context = CursorContext::new(req);
- let actual_edit = rename_command(&context, CancellationToken::none()).unwrap();
+ let actual_edit = rename_command(&context).unwrap();
let mut expected_changes = HashMap::new();
expected_changes.insert(
- uri1.as_ref().clone().into(),
+ uri1.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 1, 0, 4), "qux".into())],
);
expected_changes.insert(
- uri2.as_ref().clone().into(),
+ uri2.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 1, 0, 4), "qux".into())],
);
let expected_edit = WorkspaceEdit::new(expected_changes);
diff --git a/support/texlab/src/features/rename/entry.rs b/support/texlab/src/features/rename/entry.rs
index 35a77d926e..4861b63570 100644
--- a/support/texlab/src/features/rename/entry.rs
+++ b/support/texlab/src/features/rename/entry.rs
@@ -1,18 +1,18 @@
use std::collections::HashMap;
-use cancellation::CancellationToken;
use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit};
+use rowan::ast::AstNode;
use crate::{
features::cursor::{CursorContext, HasPosition},
- syntax::{bibtex, latex, CstNode},
+ syntax::{
+ bibtex::{self, HasName},
+ latex,
+ },
DocumentData, LineIndexExt,
};
-pub fn prepare_entry_rename<P: HasPosition>(
- context: &CursorContext<P>,
- _cancellation_token: &CancellationToken,
-) -> Option<Range> {
+pub fn prepare_entry_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> {
let (_, range) = context
.find_citation_key_word()
.or_else(|| context.find_entry_key())?;
@@ -26,44 +26,41 @@ pub fn prepare_entry_rename<P: HasPosition>(
)
}
-pub fn rename_entry(
- context: &CursorContext<RenameParams>,
- cancellation_token: &CancellationToken,
-) -> Option<WorkspaceEdit> {
- cancellation_token.result().ok()?;
- prepare_entry_rename(context, cancellation_token)?;
+pub fn rename_entry(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> {
+ prepare_entry_rename(context)?;
let (key_text, _) = context
.find_citation_key_word()
.or_else(|| context.find_entry_key())?;
let mut changes = HashMap::new();
- for document in &context.request.subset.documents {
- cancellation_token.result().ok()?;
+ for document in context.request.workspace.documents_by_uri.values() {
match &document.data {
DocumentData::Latex(data) => {
- let edits: Vec<_> = data
- .root
+ let edits: Vec<_> = latex::SyntaxNode::new_root(data.green.clone())
.descendants()
.filter_map(latex::Citation::cast)
.filter_map(|citation| citation.key_list())
.flat_map(|keys| keys.keys())
.filter(|key| key.to_string() == key_text)
- .map(|key| document.line_index.line_col_lsp_range(key.small_range()))
+ .map(|key| {
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&key))
+ })
.map(|range| TextEdit::new(range, context.request.params.new_name.clone()))
.collect();
- changes.insert(document.uri.as_ref().clone().into(), edits);
+ changes.insert(document.uri.as_ref().clone(), edits);
}
DocumentData::Bibtex(data) => {
- let edits: Vec<_> = data
- .root
+ let edits: Vec<_> = bibtex::SyntaxNode::new_root(data.green.clone())
.descendants()
.filter_map(bibtex::Entry::cast)
- .filter_map(|entry| entry.key())
- .filter(|key| key.to_string() == key_text)
- .map(|key| document.line_index.line_col_lsp_range(key.small_range()))
+ .filter_map(|entry| entry.name_token())
+ .filter(|key| key.text() == key_text)
+ .map(|key| document.line_index.line_col_lsp_range(key.text_range()))
.map(|range| TextEdit::new(range, context.request.params.new_name.clone()))
.collect();
- changes.insert(document.uri.as_ref().clone().into(), edits);
+ changes.insert(document.uri.as_ref().clone(), edits);
}
DocumentData::BuildLog(_) => {}
}
@@ -98,15 +95,15 @@ mod tests {
let request = tester.rename();
let context = CursorContext::new(request);
- let actual_edit = rename_entry(&context, CancellationToken::none()).unwrap();
+ let actual_edit = rename_entry(&context).unwrap();
let mut expected_changes = HashMap::new();
expected_changes.insert(
- uri1.as_ref().clone().into(),
+ uri1.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())],
);
expected_changes.insert(
- uri2.as_ref().clone().into(),
+ uri2.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())],
);
let expected_edit = WorkspaceEdit::new(expected_changes);
@@ -132,15 +129,15 @@ mod tests {
let request = tester.rename();
let context = CursorContext::new(request);
- let actual_edit = rename_entry(&context, CancellationToken::none()).unwrap();
+ let actual_edit = rename_entry(&context).unwrap();
let mut expected_changes = HashMap::new();
expected_changes.insert(
- uri1.as_ref().clone().into(),
+ uri1.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 9, 0, 12), "qux".into())],
);
expected_changes.insert(
- uri2.as_ref().clone().into(),
+ uri2.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(1, 6, 1, 9), "qux".into())],
);
let expected_edit = WorkspaceEdit::new(expected_changes);
diff --git a/support/texlab/src/features/rename/label.rs b/support/texlab/src/features/rename/label.rs
index a73a0064bf..f24b86810b 100644
--- a/support/texlab/src/features/rename/label.rs
+++ b/support/texlab/src/features/rename/label.rs
@@ -1,18 +1,15 @@
use std::collections::HashMap;
-use cancellation::CancellationToken;
use lsp_types::{Range, RenameParams, TextEdit, WorkspaceEdit};
+use rowan::ast::AstNode;
use crate::{
features::cursor::{CursorContext, HasPosition},
- syntax::{latex, CstNode},
+ syntax::latex,
LineIndexExt,
};
-pub fn prepare_label_rename<P: HasPosition>(
- context: &CursorContext<P>,
- _cancellation_token: &CancellationToken,
-) -> Option<Range> {
+pub fn prepare_label_rename<P: HasPosition>(context: &CursorContext<P>) -> Option<Range> {
let (_, range) = context.find_label_name_key()?;
Some(
@@ -24,24 +21,24 @@ pub fn prepare_label_rename<P: HasPosition>(
)
}
-pub fn rename_label(
- context: &CursorContext<RenameParams>,
- cancellation_token: &CancellationToken,
-) -> Option<WorkspaceEdit> {
- prepare_label_rename(context, cancellation_token)?;
+pub fn rename_label(context: &CursorContext<RenameParams>) -> Option<WorkspaceEdit> {
+ prepare_label_rename(context)?;
let (name_text, _) = context.find_label_name_key()?;
let mut changes = HashMap::new();
- for document in &context.request.subset.documents {
- cancellation_token.result().ok()?;
+ for document in context.request.workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
let mut edits = Vec::new();
- for node in data.root.descendants() {
- if let Some(range) = latex::LabelDefinition::cast(node)
+ for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
+ if let Some(range) = latex::LabelDefinition::cast(node.clone())
.and_then(|label| label.name())
.and_then(|name| name.key())
.filter(|name| name.to_string() == name_text)
- .map(|name| document.line_index.line_col_lsp_range(name.small_range()))
+ .map(|name| {
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&name))
+ })
{
edits.push(TextEdit::new(
range,
@@ -49,12 +46,16 @@ pub fn rename_label(
));
}
- latex::LabelReference::cast(node)
+ latex::LabelReference::cast(node.clone())
.and_then(|label| label.name_list())
.into_iter()
.flat_map(|label| label.keys())
.filter(|name| name.to_string() == name_text)
- .map(|name| document.line_index.line_col_lsp_range(name.small_range()))
+ .map(|name| {
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&name))
+ })
.for_each(|range| {
edits.push(TextEdit::new(
range,
@@ -62,14 +63,16 @@ pub fn rename_label(
));
});
- if let Some(label) = latex::LabelReferenceRange::cast(node) {
+ if let Some(label) = latex::LabelReferenceRange::cast(node.clone()) {
if let Some(name1) = label
.from()
.and_then(|name| name.key())
.filter(|name| name.to_string() == name_text)
{
edits.push(TextEdit::new(
- document.line_index.line_col_lsp_range(name1.small_range()),
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&name1)),
context.request.params.new_name.clone(),
));
}
@@ -80,14 +83,16 @@ pub fn rename_label(
.filter(|name| name.to_string() == name_text)
{
edits.push(TextEdit::new(
- document.line_index.line_col_lsp_range(name2.small_range()),
+ document
+ .line_index
+ .line_col_lsp_range(latex::small_range(&name2)),
context.request.params.new_name.clone(),
));
}
}
}
- changes.insert(document.uri.as_ref().clone().into(), edits);
+ changes.insert(document.uri.as_ref().clone(), edits);
}
}
@@ -119,15 +124,15 @@ mod tests {
let request = tester.rename();
let context = CursorContext::new(request);
- let actual_edit = rename_label(&context, CancellationToken::none()).unwrap();
+ let actual_edit = rename_label(&context).unwrap();
let mut expected_changes = HashMap::new();
expected_changes.insert(
- uri1.as_ref().clone().into(),
+ uri1.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 7, 0, 10), "bar".into())],
);
expected_changes.insert(
- uri2.as_ref().clone().into(),
+ uri2.as_ref().clone(),
vec![TextEdit::new(Range::new_simple(0, 5, 0, 8), "bar".into())],
);
let expected_edit = WorkspaceEdit::new(expected_changes);
diff --git a/support/texlab/src/features/semantic.rs b/support/texlab/src/features/semantic.rs
deleted file mode 100644
index 8dd2674601..0000000000
--- a/support/texlab/src/features/semantic.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-mod bibtex;
-pub mod legend;
-
-use cancellation::CancellationToken;
-use lsp_types::{SemanticTokens, SemanticTokensRangeParams};
-
-use self::bibtex::find_bibtex_semantic_tokens_range;
-
-use super::FeatureRequest;
-
-pub fn find_semantic_tokens_range(
- request: FeatureRequest<SemanticTokensRangeParams>,
- cancellation_token: &CancellationToken,
-) -> Option<SemanticTokens> {
- let mut data = Vec::new();
- find_bibtex_semantic_tokens_range(&request, &mut data, cancellation_token);
- Some(SemanticTokens {
- result_id: None,
- data,
- })
-}
diff --git a/support/texlab/src/features/semantic/bibtex.rs b/support/texlab/src/features/semantic/bibtex.rs
deleted file mode 100644
index 6d6a46b6ce..0000000000
--- a/support/texlab/src/features/semantic/bibtex.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-use cancellation::CancellationToken;
-use cstree::{NodeOrToken, TextRange};
-use lsp_types::{
- SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokensRangeParams,
-};
-
-use crate::{
- features::FeatureRequest,
- syntax::{bibtex, CstNode},
- BibtexEntryTypeCategory, LineIndex, LineIndexExt, LANGUAGE_DATA,
-};
-
-use super::legend::*;
-
-struct Context<'a> {
- line_index: &'a LineIndex,
- previous_line: u32,
- previous_character: u32,
- tokens: &'a mut Vec<SemanticToken>,
- cancellation_token: &'a CancellationToken,
-}
-
-impl<'a> Context<'a> {
- pub fn insert(
- &mut self,
- range: TextRange,
- token_type: SemanticTokenType,
- token_modifiers_bitset: ModifierSet,
- ) {
- let range = self.line_index.line_col_lsp_range(range);
-
- let mut delta_line = range.start.line;
- let mut delta_start = range.start.character;
- if !self.tokens.is_empty() {
- delta_line -= self.previous_line;
- if delta_line == 0 {
- delta_start -= self.previous_character;
- }
- }
- self.tokens.push(SemanticToken {
- delta_line,
- delta_start,
- length: range.end.character - range.start.character,
- token_type: type_index(token_type),
- token_modifiers_bitset: token_modifiers_bitset.0,
- });
-
- self.previous_line = range.start.line;
- self.previous_character = range.start.character;
- }
-}
-
-pub fn find_bibtex_semantic_tokens_range(
- request: &FeatureRequest<SemanticTokensRangeParams>,
- tokens: &mut Vec<SemanticToken>,
- cancellation_token: &CancellationToken,
-) -> Option<()> {
- let document = request.main_document();
- let data = document.data.as_bibtex()?;
-
- let mut context = Context {
- line_index: &document.line_index,
- previous_line: 0,
- previous_character: 0,
- tokens,
- cancellation_token,
- };
-
- let range = document.line_index.offset_lsp_range(request.params.range);
-
- for node in data
- .root
- .children()
- .filter(|node| node.text_range().intersect(range).is_some())
- {
- cancellation_token.result().ok()?;
- visit_junk(&mut context, node)
- .or_else(|| visit_preamble(&mut context, node))
- .or_else(|| visit_string(&mut context, node))
- .or_else(|| visit_entry(&mut context, node));
- }
-
- Some(())
-}
-
-fn visit_junk(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- let junk = bibtex::Junk::cast(node)?;
- for token in junk
- .syntax()
- .children_with_tokens()
- .filter_map(|element| element.into_token())
- .filter(|token| token.kind() != bibtex::WHITESPACE)
- {
- context.insert(token.text_range(), JUNK, ModifierSet::default());
- }
- Some(())
-}
-
-fn visit_preamble(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- let preamble = bibtex::Preamble::cast(node)?;
- for element in preamble.syntax().children_with_tokens() {
- match element {
- NodeOrToken::Node(value) => {
- visit_value(context, value);
- }
- NodeOrToken::Token(token) => match token.kind() {
- bibtex::PREAMBLE_TYPE => {
- context.insert(token.text_range(), PREAMBLE_TYPE, ModifierSet::default());
- }
- bibtex::L_CURLY | bibtex::R_CURLY => {
- context.insert(token.text_range(), CURLY, ModifierSet::default());
- }
- bibtex::L_PAREN | bibtex::R_PAREN => {
- context.insert(token.text_range(), PAREN, ModifierSet::default());
- }
- _ => {}
- },
- }
- }
-
- Some(())
-}
-
-fn visit_string(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- let string = bibtex::String::cast(node)?;
- for element in string.syntax().children_with_tokens() {
- match element {
- NodeOrToken::Node(value) => {
- visit_value(context, value);
- }
- NodeOrToken::Token(token) => match token.kind() {
- bibtex::STRING_TYPE => {
- context.insert(token.text_range(), STRING_TYPE, ModifierSet::default());
- }
- bibtex::L_CURLY | bibtex::R_CURLY => {
- context.insert(token.text_range(), CURLY, ModifierSet::default());
- }
- bibtex::L_PAREN | bibtex::R_PAREN => {
- context.insert(token.text_range(), PAREN, ModifierSet::default());
- }
- bibtex::WORD => {
- context.insert(
- token.text_range(),
- STRING_NAME,
- ModifierSet::default() | SemanticTokenModifier::DEFINITION,
- );
- }
- bibtex::EQUALITY_SIGN => {
- context.insert(token.text_range(), EQUALITY_SIGN, ModifierSet::default());
- }
- _ => {}
- },
- }
- }
-
- Some(())
-}
-
-fn visit_entry(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- let entry = bibtex::Entry::cast(node)?;
-
- for element in entry.syntax().children_with_tokens() {
- match element {
- NodeOrToken::Node(field) => {
- visit_field(context, field);
- }
- NodeOrToken::Token(token) => match token.kind() {
- bibtex::ENTRY_TYPE => {
- context.insert(
- token.text_range(),
- match LANGUAGE_DATA.find_entry_type(&token.text()[1..]) {
- Some(ty) => match ty.category {
- BibtexEntryTypeCategory::Article => ARTICLE_TYPE,
- BibtexEntryTypeCategory::Misc => MISC_TYPE,
- BibtexEntryTypeCategory::String => STRING_TYPE,
- BibtexEntryTypeCategory::Book => BOOK_TYPE,
- BibtexEntryTypeCategory::Collection => COLLECTION_TYPE,
- BibtexEntryTypeCategory::Part => PART_TYPE,
- BibtexEntryTypeCategory::Thesis => THESIS_TYPE,
- },
- None => UNKNOWN_TYPE,
- },
- ModifierSet::default(),
- );
- }
- bibtex::L_CURLY | bibtex::R_CURLY => {
- context.insert(token.text_range(), CURLY, ModifierSet::default());
- }
- bibtex::L_PAREN | bibtex::R_PAREN => {
- context.insert(token.text_range(), PAREN, ModifierSet::default());
- }
- bibtex::WORD => {
- context.insert(
- token.text_range(),
- ENTRY_KEY,
- ModifierSet::default()
- | SemanticTokenModifier::DEFINITION
- | SemanticTokenModifier::READONLY,
- );
- }
- bibtex::COMMA => {
- context.insert(token.text_range(), COMMA, ModifierSet::default());
- }
- _ => {}
- },
- }
- }
-
- Some(())
-}
-
-fn visit_field(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- context.cancellation_token.result().ok()?;
- let field = bibtex::Field::cast(node)?;
- context.insert(field.name()?.text_range(), FIELD, ModifierSet::default());
- if let Some(token) = field.equality_sign() {
- context.insert(token.text_range(), EQUALITY_SIGN, ModifierSet::default());
- }
-
- if let Some(value) = field.value() {
- visit_value(context, value.syntax());
- }
- Some(())
-}
-
-fn visit_value(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- context.cancellation_token.result().ok()?;
- let value = bibtex::Value::cast(node)?;
- for child in value.syntax().children_with_tokens() {
- match child {
- NodeOrToken::Token(token) if token.kind() == bibtex::HASH => {
- context.insert(token.text_range(), HASH, ModifierSet::default());
- }
- NodeOrToken::Token(_) => {}
- NodeOrToken::Node(token) => {
- if let Some(word) = token
- .first_child_or_token()
- .and_then(|child| child.into_token())
- {
- if word.text().chars().all(|c| c.is_ascii_digit()) {
- context.insert(
- word.text_range(),
- SemanticTokenType::NUMBER,
- ModifierSet::default(),
- );
- } else {
- context.insert(word.text_range(), STRING_NAME, ModifierSet::default());
- }
- } else {
- visit_content(context, token);
- }
- }
- }
- }
- Some(())
-}
-
-fn visit_content(context: &mut Context, node: &bibtex::SyntaxNode) -> Option<()> {
- for token in node
- .descendants_with_tokens()
- .filter_map(|element| element.into_token())
- {
- match token.kind() {
- bibtex::WORD => {
- context.insert(token.text_range(), TEXT, ModifierSet::default());
- }
- bibtex::COMMA => {
- context.insert(token.text_range(), COMMA, ModifierSet::default());
- }
- bibtex::L_CURLY | bibtex::R_CURLY => {
- context.insert(token.text_range(), CURLY, ModifierSet::default());
- }
- bibtex::L_PAREN | bibtex::R_PAREN => {
- context.insert(token.text_range(), PAREN, ModifierSet::default());
- }
- bibtex::SyntaxKind::QUOTE => {
- context.insert(token.text_range(), QUOTE, ModifierSet::default());
- }
- bibtex::SyntaxKind::EQUALITY_SIGN => {
- context.insert(token.text_range(), EQUALITY_SIGN, ModifierSet::default());
- }
- bibtex::SyntaxKind::COMMAND_NAME => {
- context.insert(token.text_range(), GENERIC_COMMAND, ModifierSet::default());
- }
- _ => {}
- }
- }
- Some(())
-}
diff --git a/support/texlab/src/features/semantic/legend.rs b/support/texlab/src/features/semantic/legend.rs
deleted file mode 100644
index 0b2a53cd38..0000000000
--- a/support/texlab/src/features/semantic/legend.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use std::ops;
-
-use lsp_types::{SemanticTokenModifier, SemanticTokenType};
-
-macro_rules! define_semantic_token_types {
- ($(($ident:ident, $string:literal)),*$(,)?) => {
- $(pub const $ident: SemanticTokenType = SemanticTokenType::new($string);)*
-
- pub const SUPPORTED_TYPES: &[SemanticTokenType] = &[
- SemanticTokenType::COMMENT,
- SemanticTokenType::NUMBER,
- $($ident),*
- ];
- };
-}
-
-define_semantic_token_types![
- (JUNK, "junk"),
- (PREAMBLE_TYPE, "preambleType"),
- (STRING_TYPE, "stringType"),
- (COMMENT_TYPE, "commentType"),
- (ARTICLE_TYPE, "articleType"),
- (BOOK_TYPE, "bookType"),
- (COLLECTION_TYPE, "collectionType"),
- (PART_TYPE, "partType"),
- (THESIS_TYPE, "thesisType"),
- (MISC_TYPE, "miscType"),
- (UNKNOWN_TYPE, "unknownType"),
- (CURLY, "curly"),
- (BRACK, "brack"),
- (PAREN, "paren"),
- (COMMA, "comma"),
- (ENTRY_KEY, "entryKey"),
- (FIELD, "field"),
- (TEXT, "text"),
- (STRING_NAME, "string"),
- (EQUALITY_SIGN, "equalitySign"),
- (HASH, "hash"),
- (QUOTE, "quote"),
- (GENERIC_COMMAND, "genericCommand"),
-];
-
-macro_rules! define_semantic_token_modifiers {
- ($(($ident:ident, $string:literal)),*$(,)?) => {
- $(pub const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
-
- pub const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
- SemanticTokenModifier::DEFAULT_LIBRARY,
- SemanticTokenModifier::DECLARATION,
- SemanticTokenModifier::DEFINITION,
- SemanticTokenModifier::READONLY,
- $($ident),*
- ];
- };
-}
-
-define_semantic_token_modifiers![(ITALIC, "italic"),];
-
-pub fn type_index(ty: SemanticTokenType) -> u32 {
- SUPPORTED_TYPES.iter().position(|t| *t == ty).unwrap() as u32
-}
-
-#[derive(Default)]
-pub struct ModifierSet(pub u32);
-
-impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
- fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
- let index = SUPPORTED_MODIFIERS
- .iter()
- .position(|modifier| modifier == &rhs)
- .unwrap();
- self.0 |= 1 << index;
- }
-}
-
-impl ops::BitOr<SemanticTokenModifier> for ModifierSet {
- type Output = ModifierSet;
-
- fn bitor(mut self, rhs: SemanticTokenModifier) -> Self::Output {
- self |= rhs;
- self
- }
-}
diff --git a/support/texlab/src/features/symbol.rs b/support/texlab/src/features/symbol.rs
index 349b6d0a37..42b9d03d3f 100644
--- a/support/texlab/src/features/symbol.rs
+++ b/support/texlab/src/features/symbol.rs
@@ -5,12 +5,12 @@ mod types;
use std::{cmp::Reverse, sync::Arc};
-use cancellation::CancellationToken;
use lsp_types::{
- DocumentSymbolParams, DocumentSymbolResponse, SymbolInformation, WorkspaceSymbolParams,
+ DocumentSymbolParams, DocumentSymbolResponse, PartialResultParams, SymbolInformation,
+ TextDocumentIdentifier, WorkDoneProgressParams, WorkspaceSymbolParams,
};
-use crate::{ClientCapabilitiesExt, Uri, Workspace};
+use crate::{ClientCapabilitiesExt, Workspace};
use self::{
bibtex::find_bibtex_symbols, latex::find_latex_symbols, project_order::ProjectOrdering,
@@ -18,18 +18,14 @@ use self::{
use super::FeatureRequest;
-pub fn find_document_symbols(
- req: FeatureRequest<DocumentSymbolParams>,
- token: &CancellationToken,
-) -> DocumentSymbolResponse {
+pub fn find_document_symbols(req: FeatureRequest<DocumentSymbolParams>) -> DocumentSymbolResponse {
let mut buf = Vec::new();
- find_latex_symbols(&req.subset, &mut buf, token);
- find_bibtex_symbols(&req.subset, &mut buf, token);
+ find_latex_symbols(&req, &mut buf);
+ find_bibtex_symbols(&req, &mut buf);
if req
- .context
+ .workspace
+ .environment
.client_capabilities
- .lock()
- .unwrap()
.has_hierarchical_document_symbol_support()
{
DocumentSymbolResponse::Nested(
@@ -42,11 +38,11 @@ pub fn find_document_symbols(
for symbol in buf {
symbol.flatten(&mut new_buf);
}
- let mut new_buf = new_buf
+ let mut new_buf: Vec<_> = new_buf
.into_iter()
.map(|symbol| symbol.into_symbol_info(req.main_document().uri.as_ref().clone()))
.collect();
- sort_symbols(req.workspace.as_ref(), &mut new_buf);
+ sort_symbols(&req.workspace, &mut new_buf);
DocumentSymbolResponse::Flat(new_buf)
}
}
@@ -58,29 +54,36 @@ struct WorkspaceSymbol {
}
pub fn find_workspace_symbols(
- workspace: &dyn Workspace,
+ workspace: &Workspace,
params: &WorkspaceSymbolParams,
- token: &CancellationToken,
) -> Vec<SymbolInformation> {
let mut symbols = Vec::new();
- for document in workspace.documents() {
- if let Some(subset) = workspace.subset(Arc::clone(&document.uri)) {
- let mut buf = Vec::new();
- find_latex_symbols(&subset, &mut buf, token);
- find_bibtex_symbols(&subset, &mut buf, token);
- let mut new_buf = Vec::new();
+ for document in workspace.documents_by_uri.values() {
+ let request = FeatureRequest {
+ uri: Arc::clone(&document.uri),
+ params: DocumentSymbolParams {
+ text_document: TextDocumentIdentifier::new(document.uri.as_ref().clone()),
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ },
+ workspace: workspace.slice(&document.uri),
+ };
- for symbol in buf {
- symbol.flatten(&mut new_buf);
- }
+ let mut buf = Vec::new();
+ find_latex_symbols(&request, &mut buf);
+ find_bibtex_symbols(&request, &mut buf);
+ let mut new_buf = Vec::new();
- for symbol in new_buf {
- symbols.push(WorkspaceSymbol {
- search_text: symbol.search_text(),
- info: symbol.into_symbol_info(document.uri.as_ref().clone()),
- });
- }
+ for symbol in buf {
+ symbol.flatten(&mut new_buf);
+ }
+
+ for symbol in new_buf {
+ symbols.push(WorkspaceSymbol {
+ search_text: symbol.search_text(),
+ info: symbol.into_symbol_info(document.uri.as_ref().clone()),
+ });
}
}
@@ -107,16 +110,16 @@ pub fn find_workspace_symbols(
filtered
}
-fn sort_symbols(workspace: &dyn Workspace, symbols: &mut Vec<SymbolInformation>) {
+fn sort_symbols(workspace: &Workspace, symbols: &mut [SymbolInformation]) {
let ordering = ProjectOrdering::from(workspace);
symbols.sort_by(|left, right| {
let left_key = (
- ordering.get(&Uri::from(left.location.uri.clone())),
+ ordering.get(&left.location.uri),
left.location.range.start,
Reverse(left.location.range.end),
);
let right_key = (
- ordering.get(&Uri::from(right.location.uri.clone())),
+ ordering.get(&right.location.uri),
right.location.range.start,
Reverse(right.location.range.end),
);
diff --git a/support/texlab/src/features/symbol/bibtex.rs b/support/texlab/src/features/symbol/bibtex.rs
index db555a7176..bbc9026b0b 100644
--- a/support/texlab/src/features/symbol/bibtex.rs
+++ b/support/texlab/src/features/symbol/bibtex.rs
@@ -1,30 +1,24 @@
-use cancellation::CancellationToken;
+use lsp_types::DocumentSymbolParams;
+use rowan::ast::AstNode;
use crate::{
- syntax::{
- bibtex::{self, HasType},
- CstNode,
- },
- BibtexEntryTypeCategory, LineIndexExt, WorkspaceSubset, LANGUAGE_DATA,
+ features::FeatureRequest,
+ syntax::bibtex::{self, HasName, HasType},
+ BibtexEntryTypeCategory, LineIndexExt, LANGUAGE_DATA,
};
use super::types::{InternalSymbol, InternalSymbolKind};
pub fn find_bibtex_symbols(
- subset: &WorkspaceSubset,
+ request: &FeatureRequest<DocumentSymbolParams>,
buf: &mut Vec<InternalSymbol>,
- token: &CancellationToken,
) -> Option<()> {
- let main_document = subset.documents.first()?;
+ let main_document = request.main_document();
let data = main_document.data.as_bibtex()?;
- for node in data.root.children() {
- if token.is_canceled() {
- return None;
- }
-
- if let Some(string) = bibtex::String::cast(node) {
- if let Some(name) = string.name() {
+ for node in bibtex::SyntaxNode::new_root(data.green.clone()).children() {
+ if let Some(string) = bibtex::StringDef::cast(node.clone()) {
+ if let Some(name) = string.name_token() {
buf.push(InternalSymbol {
name: name.text().into(),
label: None,
@@ -32,7 +26,7 @@ pub fn find_bibtex_symbols(
deprecated: false,
full_range: main_document
.line_index
- .line_col_lsp_range(string.small_range()),
+ .line_col_lsp_range(string.syntax().text_range()),
selection_range: main_document
.line_index
.line_col_lsp_range(name.text_range()),
@@ -40,11 +34,11 @@ pub fn find_bibtex_symbols(
})
}
} else if let Some(entry) = bibtex::Entry::cast(node) {
- if let Some(ty) = entry.ty() {
- if let Some(key) = entry.key() {
+ if let Some(ty) = entry.type_token() {
+ if let Some(key) = entry.name_token() {
let mut children = Vec::new();
for field in entry.fields() {
- if let Some(name) = field.name() {
+ if let Some(name) = field.name_token() {
let symbol = InternalSymbol {
name: name.text().to_string(),
label: None,
@@ -52,7 +46,7 @@ pub fn find_bibtex_symbols(
deprecated: false,
full_range: main_document
.line_index
- .line_col_lsp_range(field.small_range()),
+ .line_col_lsp_range(field.syntax().text_range()),
selection_range: main_document
.line_index
.line_col_lsp_range(name.text_range()),
@@ -74,10 +68,10 @@ pub fn find_bibtex_symbols(
deprecated: false,
full_range: main_document
.line_index
- .line_col_lsp_range(entry.small_range()),
+ .line_col_lsp_range(entry.syntax().text_range()),
selection_range: main_document
.line_index
- .line_col_lsp_range(key.small_range()),
+ .line_col_lsp_range(key.text_range()),
children,
});
}
diff --git a/support/texlab/src/features/symbol/latex.rs b/support/texlab/src/features/symbol/latex.rs
index ec549c87fe..8a88ffb0d9 100644
--- a/support/texlab/src/features/symbol/latex.rs
+++ b/support/texlab/src/features/symbol/latex.rs
@@ -1,51 +1,38 @@
use std::str::FromStr;
-use cancellation::CancellationToken;
-use lsp_types::Range;
+use lsp_types::{DocumentSymbolParams, Range};
+use rowan::ast::AstNode;
use smol_str::SmolStr;
use titlecase::titlecase;
use crate::{
+ features::FeatureRequest,
find_caption_by_parent, find_label_number,
- syntax::{
- latex::{self, HasBrack, HasCurly},
- CstNode,
- },
- LabelledFloatKind, LatexDocumentData, LineIndexExt, WorkspaceSubset, LANGUAGE_DATA,
+ syntax::latex::{self, HasBrack, HasCurly},
+ LabelledFloatKind, LatexDocumentData, LineIndexExt, LANGUAGE_DATA,
};
use super::types::{InternalSymbol, InternalSymbolKind};
pub fn find_latex_symbols(
- subset: &WorkspaceSubset,
+ request: &FeatureRequest<DocumentSymbolParams>,
buf: &mut Vec<InternalSymbol>,
- token: &CancellationToken,
) -> Option<()> {
- let main_document = subset
- .documents
- .first()
- .filter(|document| document.uri.as_str().ends_with(".tex"))?;
-
- let data = main_document.data.as_latex()?;
- let mut context = Context {
- subset,
- data,
- token,
- };
+ let data = request.main_document().data.as_latex()?;
+ let mut context = Context { request, data };
- let root = &context.data.root;
- let mut symbols = visit(&mut context, root);
+ let root = context.data.green.clone();
+ let mut symbols = visit(&mut context, latex::SyntaxNode::new_root(root));
buf.append(&mut symbols);
Some(())
}
struct Context<'a> {
- subset: &'a WorkspaceSubset,
+ request: &'a FeatureRequest<DocumentSymbolParams>,
data: &'a LatexDocumentData,
- token: &'a CancellationToken,
}
-fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol> {
+fn visit(context: &mut Context, node: latex::SyntaxNode) -> Vec<InternalSymbol> {
let symbol = match node.kind() {
latex::PART
| latex::CHAPTER
@@ -53,10 +40,10 @@ fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol>
| latex::SUBSECTION
| latex::SUBSUBSECTION
| latex::PARAGRAPH
- | latex::SUBPARAGRAPH => visit_section(context, node),
- latex::ENUM_ITEM => visit_enum_item(context, node),
- latex::EQUATION => visit_equation(context, node),
- latex::ENVIRONMENT => latex::Environment::cast(node)
+ | latex::SUBPARAGRAPH => visit_section(context, node.clone()),
+ latex::ENUM_ITEM => visit_enum_item(context, node.clone()),
+ latex::EQUATION => visit_equation(context, node.clone()),
+ latex::ENVIRONMENT => latex::Environment::cast(node.clone())
.and_then(|env| env.begin())
.and_then(|begin| begin.name())
.and_then(|name| name.key())
@@ -67,17 +54,17 @@ fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol>
.iter()
.any(|env| env == &name)
{
- visit_equation_environment(context, node)
+ visit_equation_environment(context, node.clone())
} else if LANGUAGE_DATA
.enum_environments
.iter()
.any(|env| env == &name)
{
- visit_enumeration(context, node, &name)
+ visit_enumeration(context, node.clone(), &name)
} else if let Ok(float_kind) = LabelledFloatKind::from_str(&name) {
- visit_float(context, node, float_kind)
+ visit_float(context, node.clone(), float_kind)
} else {
- visit_theorem(context, node, &name)
+ visit_theorem(context, node.clone(), &name)
}
}),
_ => None,
@@ -86,10 +73,6 @@ fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol>
match symbol {
Some(mut parent) => {
for child in node.children() {
- if context.token.is_canceled() {
- break;
- }
-
parent.children.append(&mut visit(context, child));
}
vec![parent]
@@ -97,9 +80,6 @@ fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol>
None => {
let mut symbols = Vec::new();
for child in node.children() {
- if context.token.is_canceled() {
- break;
- }
symbols.append(&mut visit(context, child));
}
symbols
@@ -107,14 +87,13 @@ fn visit(context: &mut Context, node: &latex::SyntaxNode) -> Vec<InternalSymbol>
}
}
-fn visit_section(context: &mut Context, node: &latex::SyntaxNode) -> Option<InternalSymbol> {
+fn visit_section(context: &mut Context, node: latex::SyntaxNode) -> Option<InternalSymbol> {
let section = latex::Section::cast(node)?;
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(section.small_range());
+ .line_col_lsp_range(latex::small_range(&section));
let group = section.name()?;
let group_text = group.content_text()?;
@@ -127,7 +106,7 @@ fn visit_section(context: &mut Context, node: &latex::SyntaxNode) -> Option<Inte
}) => {
let name = match number {
Some(number) => format!("{} {}", number, group_text),
- None => group_text.to_string(),
+ None => group_text,
};
InternalSymbol {
@@ -141,7 +120,7 @@ fn visit_section(context: &mut Context, node: &latex::SyntaxNode) -> Option<Inte
}
}
None => InternalSymbol {
- name: group_text.to_string(),
+ name: group_text,
label: None,
kind: InternalSymbolKind::Section,
deprecated: false,
@@ -153,8 +132,8 @@ fn visit_section(context: &mut Context, node: &latex::SyntaxNode) -> Option<Inte
Some(symbol)
}
-fn visit_enum_item(context: &mut Context, node: &latex::SyntaxNode) -> Option<InternalSymbol> {
- let enum_item = latex::EnumItem::cast(node)?;
+fn visit_enum_item(context: &mut Context, node: latex::SyntaxNode) -> Option<InternalSymbol> {
+ let enum_item = latex::EnumItem::cast(node.clone())?;
if !enum_item
.syntax()
.ancestors()
@@ -173,18 +152,17 @@ fn visit_enum_item(context: &mut Context, node: &latex::SyntaxNode) -> Option<In
}
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(enum_item.small_range());
+ .line_col_lsp_range(latex::small_range(&enum_item));
let name = enum_item
.label()
.and_then(|label| label.content_text())
.unwrap_or_else(|| "Item".to_string());
- let symbol = match find_label_by_parent(context, node) {
+ let symbol = match find_label_by_parent(context, &node) {
Some(NumberedLabel {
name: label,
range: selection_range,
@@ -199,7 +177,7 @@ fn visit_enum_item(context: &mut Context, node: &latex::SyntaxNode) -> Option<In
children: Vec::new(),
},
None => InternalSymbol {
- name: name.to_string(),
+ name,
label: None,
kind: InternalSymbolKind::EnumerationItem,
deprecated: false,
@@ -211,38 +189,36 @@ fn visit_enum_item(context: &mut Context, node: &latex::SyntaxNode) -> Option<In
Some(symbol)
}
-fn visit_equation(context: &mut Context, node: &latex::SyntaxNode) -> Option<InternalSymbol> {
+fn visit_equation(context: &mut Context, node: latex::SyntaxNode) -> Option<InternalSymbol> {
let equation = latex::Equation::cast(node)?;
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(equation.small_range());
+ .line_col_lsp_range(latex::small_range(&equation));
- make_equation_symbol(context, node, full_range)
+ make_equation_symbol(context, equation.syntax(), full_range)
}
fn visit_equation_environment(
context: &mut Context,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<InternalSymbol> {
let environment = latex::Environment::cast(node)?;
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(environment.small_range());
+ .line_col_lsp_range(latex::small_range(&environment));
- make_equation_symbol(context, node, full_range)
+ make_equation_symbol(context, environment.syntax(), full_range)
}
fn make_equation_symbol(
context: &mut Context,
- node: &cstree::ResolvedNode<latex::Language>,
+ node: &latex::SyntaxNode,
full_range: Range,
) -> Option<InternalSymbol> {
let symbol = match find_label_by_parent(context, node) {
@@ -281,16 +257,15 @@ fn make_equation_symbol(
fn visit_enumeration(
context: &mut Context,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
env_name: &str,
) -> Option<InternalSymbol> {
let environment = latex::Environment::cast(node)?;
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(environment.small_range());
+ .line_col_lsp_range(latex::small_range(&environment));
let name = titlecase(env_name);
let symbol = match find_label_by_parent(context, environment.syntax()) {
@@ -329,16 +304,15 @@ fn visit_enumeration(
fn visit_float(
context: &mut Context,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
float_kind: LabelledFloatKind,
) -> Option<InternalSymbol> {
let environment = latex::Environment::cast(node)?;
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(environment.small_range());
+ .line_col_lsp_range(latex::small_range(&environment));
let (float_kind, symbol_kind) = match float_kind {
LabelledFloatKind::Algorithm => ("Algorithm", InternalSymbolKind::Algorithm),
@@ -385,13 +359,14 @@ fn visit_float(
fn visit_theorem(
context: &mut Context,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
environment_name: &str,
) -> Option<InternalSymbol> {
let definition = context
- .subset
- .documents
- .iter()
+ .request
+ .workspace
+ .documents_by_uri
+ .values()
.filter_map(|document| document.data.as_latex())
.find_map(|data| {
data.extras
@@ -408,11 +383,10 @@ fn visit_theorem(
.and_then(|option| option.content_text());
let full_range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(node.small_range());
+ .line_col_lsp_range(latex::small_range(&node));
let symbol = match find_label_by_parent(context, node.syntax()) {
Some(NumberedLabel {
@@ -426,7 +400,7 @@ fn visit_theorem(
}
(Some(number), None) => format!("{} {}", definition.description, number),
(None, Some(desc)) => format!("{} ({})", definition.description, desc),
- (None, None) => definition.description.clone(),
+ (None, None) => definition.description,
};
InternalSymbol {
@@ -442,7 +416,7 @@ fn visit_theorem(
None => {
let name = match theorem_description {
Some(desc) => format!("{} ({})", definition.description, desc),
- None => definition.description.to_string(),
+ None => definition.description,
};
InternalSymbol {
name,
@@ -473,13 +447,12 @@ fn find_label_by_parent(
let name = node.name()?.key()?.to_string();
let range = context
- .subset
- .documents
- .first()?
+ .request
+ .main_document()
.line_index
- .line_col_lsp_range(node.small_range());
+ .line_col_lsp_range(latex::small_range(&node));
- let number = find_label_number(&context.subset, &name);
+ let number = find_label_number(&context.request.workspace, &name);
Some(NumberedLabel {
name: name.to_string(),
range,
diff --git a/support/texlab/src/features/symbol/project_order.rs b/support/texlab/src/features/symbol/project_order.rs
index 08b43ad50c..794447ecf6 100644
--- a/support/texlab/src/features/symbol/project_order.rs
+++ b/support/texlab/src/features/symbol/project_order.rs
@@ -1,17 +1,18 @@
use std::{sync::Arc, usize};
+use lsp_types::Url;
use petgraph::{algo::tarjan_scc, Directed, Graph};
use rustc_hash::FxHashSet;
-use crate::{Uri, Workspace, WorkspaceSubset};
+use crate::{Document, Workspace};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ProjectOrdering {
- ordering: Vec<Arc<Uri>>,
+ ordering: Vec<Arc<Url>>,
}
impl ProjectOrdering {
- pub fn get(&self, uri: &Uri) -> usize {
+ pub fn get(&self, uri: &Url) -> usize {
self.ordering
.iter()
.position(|u| u.as_ref() == uri)
@@ -19,22 +20,22 @@ impl ProjectOrdering {
}
}
-impl From<&dyn Workspace> for ProjectOrdering {
- fn from(workspace: &dyn Workspace) -> Self {
+impl From<&Workspace> for ProjectOrdering {
+ fn from(workspace: &Workspace) -> Self {
let mut ordering = Vec::new();
- let uris: FxHashSet<Arc<Uri>> = workspace
- .documents()
- .into_iter()
+ let uris: FxHashSet<Arc<Url>> = workspace
+ .documents_by_uri
+ .values()
.map(|document| Arc::clone(&document.uri))
.collect();
let comps = connected_components(workspace);
for comp in comps {
- let graph = build_dependency_graph(&comp);
+ let (graph, documents) = build_dependency_graph(&comp);
let mut visited = FxHashSet::default();
let root_index = *graph.node_weight(tarjan_scc(&graph)[0][0]).unwrap();
- let mut stack = vec![Arc::clone(&comp.documents[root_index].uri)];
+ let mut stack = vec![Arc::clone(&documents[root_index].uri)];
while let Some(uri) = stack.pop() {
if !visited.insert(Arc::clone(&uri)) {
@@ -42,7 +43,7 @@ impl From<&dyn Workspace> for ProjectOrdering {
}
ordering.push(Arc::clone(&uri));
- if let Some(document) = workspace.get(&uri) {
+ if let Some(document) = workspace.documents_by_uri.get(&uri) {
if let Some(data) = document.data.as_latex() {
for link in data.extras.explicit_links.iter().rev() {
for target in &link.targets {
@@ -60,35 +61,33 @@ impl From<&dyn Workspace> for ProjectOrdering {
}
}
-fn connected_components(workspace: &dyn Workspace) -> Vec<WorkspaceSubset> {
+fn connected_components(workspace: &Workspace) -> Vec<Workspace> {
let mut components = Vec::new();
let mut visited = FxHashSet::default();
- for root_document in workspace.documents() {
+ for root_document in workspace.documents_by_uri.values() {
if !visited.insert(Arc::clone(&root_document.uri)) {
continue;
}
- let subset = workspace.subset(Arc::clone(&root_document.uri)).unwrap();
- for document in &subset.documents {
- visited.insert(Arc::clone(&document.uri));
+ let slice = workspace.slice(&root_document.uri);
+ for uri in slice.documents_by_uri.keys() {
+ visited.insert(Arc::clone(uri));
}
- components.push(subset);
+ components.push(slice);
}
components
}
-fn build_dependency_graph(subset: &WorkspaceSubset) -> Graph<usize, (), Directed> {
+fn build_dependency_graph(workspace: &Workspace) -> (Graph<usize, (), Directed>, Vec<&Document>) {
let mut graph = Graph::new();
- let nodes: Vec<_> = (0..subset.documents.len())
- .map(|i| graph.add_node(i))
- .collect();
+ let documents: Vec<_> = workspace.documents_by_uri.values().collect();
+ let nodes: Vec<_> = (0..documents.len()).map(|i| graph.add_node(i)).collect();
- for (i, document) in subset.documents.iter().enumerate() {
+ for (i, document) in documents.iter().enumerate() {
if let Some(data) = document.data.as_latex() {
for link in &data.extras.explicit_links {
for target in &link.targets {
- if let Some(j) = subset
- .documents
+ if let Some(j) = documents
.iter()
.position(|document| document.uri.as_ref() == target.as_ref())
{
@@ -99,45 +98,43 @@ fn build_dependency_graph(subset: &WorkspaceSubset) -> Graph<usize, (), Directed
}
}
}
- graph
+
+ (graph, documents)
}
#[cfg(test)]
mod tests {
+ use std::sync::Arc;
+
use anyhow::Result;
- use crate::{create_workspace_fast, DocumentLanguage, ServerContext, WorkspaceSource};
+ use crate::DocumentLanguage;
use super::*;
#[test]
fn test_no_cycles() -> Result<()> {
- let workspace: Arc<dyn Workspace> = Arc::new(create_workspace_fast(Arc::new(
- ServerContext::new(std::env::temp_dir()),
- ))?);
+ let mut workspace = Workspace::default();
let a = workspace.open(
- Arc::new(Uri::parse("http://example.com/a.tex")?),
- "".into(),
+ Arc::new(Url::parse("http://example.com/a.tex")?),
+ Arc::new(String::new()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let b = workspace.open(
- Arc::new(Uri::parse("http://example.com/b.tex")?),
- "".into(),
+ Arc::new(Url::parse("http://example.com/b.tex")?),
+ Arc::new(String::new()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let c = workspace.open(
- Arc::new(Uri::parse("http://example.com/c.tex")?),
- r#"\include{b}\include{a}"#.into(),
+ Arc::new(Url::parse("http://example.com/c.tex")?),
+ Arc::new(r#"\include{b}\include{a}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
- let ordering = ProjectOrdering::from(workspace.as_ref());
+ let ordering = ProjectOrdering::from(&workspace);
assert_eq!(ordering.get(&a.uri), 2);
assert_eq!(ordering.get(&b.uri), 1);
@@ -147,32 +144,27 @@ mod tests {
#[test]
fn test_cycles() -> Result<()> {
- let workspace: Arc<dyn Workspace> = Arc::new(create_workspace_fast(Arc::new(
- ServerContext::new(std::env::temp_dir()),
- ))?);
+ let mut workspace = Workspace::default();
let a = workspace.open(
- Arc::new(Uri::parse("http://example.com/a.tex")?),
- r#"\include{b}"#.into(),
+ Arc::new(Url::parse("http://example.com/a.tex")?),
+ Arc::new(r#"\include{b}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let b = workspace.open(
- Arc::new(Uri::parse("http://example.com/b.tex")?),
- r#"\include{a}"#.into(),
+ Arc::new(Url::parse("http://example.com/b.tex")?),
+ Arc::new(r#"\include{a}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let c = workspace.open(
- Arc::new(Uri::parse("http://example.com/c.tex")?),
- r#"\include{a}"#.into(),
+ Arc::new(Url::parse("http://example.com/c.tex")?),
+ Arc::new(r#"\include{a}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
- let ordering = ProjectOrdering::from(workspace.as_ref());
+ let ordering = ProjectOrdering::from(&workspace);
assert_eq!(ordering.get(&a.uri), 1);
assert_eq!(ordering.get(&b.uri), 2);
@@ -182,39 +174,33 @@ mod tests {
#[test]
fn test_multiple_roots() -> Result<()> {
- let workspace: Arc<dyn Workspace> = Arc::new(create_workspace_fast(Arc::new(
- ServerContext::new(std::env::temp_dir()),
- ))?);
+ let mut workspace = Workspace::default();
let a = workspace.open(
- Arc::new(Uri::parse("http://example.com/a.tex")?),
- r#"\include{b}"#.into(),
+ Arc::new(Url::parse("http://example.com/a.tex")?),
+ Arc::new(r#"\include{b}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let b = workspace.open(
- Arc::new(Uri::parse("http://example.com/b.tex")?),
- r#""#.into(),
+ Arc::new(Url::parse("http://example.com/b.tex")?),
+ Arc::new(r#""#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let c = workspace.open(
- Arc::new(Uri::parse("http://example.com/c.tex")?),
- r#""#.into(),
+ Arc::new(Url::parse("http://example.com/c.tex")?),
+ Arc::new(r#""#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
let d = workspace.open(
- Arc::new(Uri::parse("http://example.com/d.tex")?),
- r#"\include{c}"#.into(),
+ Arc::new(Url::parse("http://example.com/d.tex")?),
+ Arc::new(r#"\include{c}"#.to_string()),
DocumentLanguage::Latex,
- WorkspaceSource::Client,
- );
+ )?;
- let ordering = ProjectOrdering::from(workspace.as_ref());
+ let ordering = ProjectOrdering::from(&workspace);
assert!(ordering.get(&a.uri) < ordering.get(&b.uri));
assert!(ordering.get(&d.uri) < ordering.get(&c.uri));
diff --git a/support/texlab/src/features/symbol/types.rs b/support/texlab/src/features/symbol/types.rs
index bb00a932d8..88e7ffcb86 100644
--- a/support/texlab/src/features/symbol/types.rs
+++ b/support/texlab/src/features/symbol/types.rs
@@ -1,6 +1,6 @@
-use lsp_types::{DocumentSymbol, Location, Range, SymbolInformation, SymbolKind};
+use lsp_types::{DocumentSymbol, Location, Range, SymbolInformation, SymbolKind, Url};
-use crate::{features::lsp_kinds::Structure, BibtexEntryTypeCategory, Uri};
+use crate::{features::lsp_kinds::Structure, BibtexEntryTypeCategory};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InternalSymbolKind {
@@ -95,13 +95,13 @@ impl InternalSymbol {
}
}
- pub fn into_symbol_info(self, uri: Uri) -> SymbolInformation {
+ pub fn into_symbol_info(self, uri: Url) -> SymbolInformation {
#[allow(deprecated)]
SymbolInformation {
name: self.name,
kind: self.kind.into_symbol_kind(),
deprecated: Some(self.deprecated),
- location: Location::new(uri.into(), self.full_range),
+ location: Location::new(uri, self.full_range),
container_name: None,
tags: None,
}
diff --git a/support/texlab/src/label.rs b/support/texlab/src/label.rs
index 3d40b1620c..9212c49222 100644
--- a/support/texlab/src/label.rs
+++ b/support/texlab/src/label.rs
@@ -1,14 +1,11 @@
use std::str::FromStr;
-use cstree::TextRange;
use lsp_types::{MarkupContent, MarkupKind};
+use rowan::{ast::AstNode, TextRange};
use crate::{
- syntax::{
- latex::{self, HasBrack, HasCurly},
- CstNode,
- },
- WorkspaceSubset, LANGUAGE_DATA,
+ syntax::latex::{self, HasBrack, HasCurly},
+ Workspace, LANGUAGE_DATA,
};
use self::LabelledObject::*;
@@ -126,32 +123,34 @@ impl RenderedLabel {
}
}
-pub fn render_label<'a>(
- subset: &'a WorkspaceSubset,
+pub fn render_label(
+ workspace: &Workspace,
label_name: &str,
- mut label: Option<latex::LabelDefinition<'a>>,
+ mut label: Option<latex::LabelDefinition>,
) -> Option<RenderedLabel> {
- let mut number = find_label_number(subset, label_name).map(ToString::to_string);
+ let mut number = find_label_number(workspace, label_name).map(ToString::to_string);
- for document in &subset.documents {
+ for document in workspace.documents_by_uri.values() {
if let Some(data) = document.data.as_latex() {
- label = label.or_else(|| find_label_definition(&data.root, label_name));
+ label = label.or_else(|| {
+ find_label_definition(&latex::SyntaxNode::new_root(data.green.clone()), label_name)
+ });
}
}
label?.syntax().ancestors().find_map(|parent| {
- render_label_float(parent, &mut number)
- .or_else(|| render_label_section(parent, &mut number))
- .or_else(|| render_label_enum_item(parent, &mut number))
- .or_else(|| render_label_equation(parent, &mut number))
- .or_else(|| render_label_theorem(subset, parent, &mut number))
+ render_label_float(parent.clone(), &mut number)
+ .or_else(|| render_label_section(parent.clone(), &mut number))
+ .or_else(|| render_label_enum_item(parent.clone(), &mut number))
+ .or_else(|| render_label_equation(parent.clone(), &mut number))
+ .or_else(|| render_label_theorem(workspace, parent, &mut number))
})
}
-pub fn find_label_definition<'a>(
- root: &'a latex::SyntaxNode,
+pub fn find_label_definition(
+ root: &latex::SyntaxNode,
label_name: &str,
-) -> Option<latex::LabelDefinition<'a>> {
+) -> Option<latex::LabelDefinition> {
root.descendants()
.filter_map(latex::LabelDefinition::cast)
.find(|label| {
@@ -164,8 +163,8 @@ pub fn find_label_definition<'a>(
})
}
-pub fn find_label_number<'a>(subset: &'a WorkspaceSubset, label_name: &str) -> Option<&'a str> {
- subset.documents.iter().find_map(|document| {
+pub fn find_label_number<'a>(workspace: &'a Workspace, label_name: &str) -> Option<&'a str> {
+ workspace.documents_by_uri.values().find_map(|document| {
document
.data
.as_latex()
@@ -175,22 +174,22 @@ pub fn find_label_number<'a>(subset: &'a WorkspaceSubset, label_name: &str) -> O
}
fn render_label_float(
- parent: &latex::SyntaxNode,
+ parent: latex::SyntaxNode,
number: &mut Option<String>,
) -> Option<RenderedLabel> {
- let environment = latex::Environment::cast(parent)?;
+ let environment = latex::Environment::cast(parent.clone())?;
let environment_name = environment.begin()?.name()?.key()?.to_string();
let kind = LabelledFloatKind::from_str(&environment_name).ok()?;
let caption = find_caption_by_parent(&parent)?;
Some(RenderedLabel {
- range: environment.small_range(),
+ range: latex::small_range(&environment),
number: number.take(),
object: LabelledObject::Float { caption, kind },
})
}
fn render_label_section(
- parent: &latex::SyntaxNode,
+ parent: latex::SyntaxNode,
number: &mut Option<String>,
) -> Option<RenderedLabel> {
let section = latex::Section::cast(parent)?;
@@ -198,7 +197,7 @@ fn render_label_section(
let text = text_group.content_text()?;
Some(RenderedLabel {
- range: section.small_range(),
+ range: latex::small_range(&section),
number: number.take(),
object: LabelledObject::Section {
prefix: match section.syntax().kind() {
@@ -217,12 +216,12 @@ fn render_label_section(
}
fn render_label_enum_item(
- parent: &latex::SyntaxNode,
+ parent: latex::SyntaxNode,
number: &mut Option<String>,
) -> Option<RenderedLabel> {
let enum_item = latex::EnumItem::cast(parent)?;
Some(RenderedLabel {
- range: enum_item.small_range(),
+ range: latex::small_range(&enum_item),
number: enum_item
.label()
.and_then(|number| number.content_text())
@@ -232,7 +231,7 @@ fn render_label_enum_item(
}
fn render_label_equation(
- parent: &latex::SyntaxNode,
+ parent: latex::SyntaxNode,
number: &mut Option<String>,
) -> Option<RenderedLabel> {
let environment = latex::Environment::cast(parent)?;
@@ -247,15 +246,15 @@ fn render_label_equation(
}
Some(RenderedLabel {
- range: environment.small_range(),
+ range: latex::small_range(&environment),
number: number.take(),
object: LabelledObject::Equation,
})
}
fn render_label_theorem(
- subset: &WorkspaceSubset,
- parent: &latex::SyntaxNode,
+ workspace: &Workspace,
+ parent: latex::SyntaxNode,
number: &mut Option<String>,
) -> Option<RenderedLabel> {
let environment = latex::Environment::cast(parent)?;
@@ -264,7 +263,7 @@ fn render_label_theorem(
let environment_name = begin.name()?.key()?.to_string();
- let theorem = subset.documents.iter().find_map(|document| {
+ let theorem = workspace.documents_by_uri.values().find_map(|document| {
document.data.as_latex().and_then(|data| {
data.extras
.theorem_environments
@@ -274,7 +273,7 @@ fn render_label_theorem(
})?;
Some(RenderedLabel {
- range: environment.small_range(),
+ range: latex::small_range(&environment),
number: number.take(),
object: LabelledObject::Theorem {
kind: theorem.description.clone(),
diff --git a/support/texlab/src/lib.rs b/support/texlab/src/lib.rs
index 0441c1a1d7..ce52bee1da 100644
--- a/support/texlab/src/lib.rs
+++ b/support/texlab/src/lib.rs
@@ -1,13 +1,12 @@
mod capabilities;
-#[cfg(feature = "citation")]
pub mod citation;
mod client;
pub mod component_db;
-mod config;
-mod context;
pub mod diagnostics;
mod dispatch;
pub mod distro;
+mod document;
+mod environment;
pub mod features;
mod label;
mod lang_data;
@@ -19,12 +18,12 @@ mod range;
mod req_queue;
mod server;
pub mod syntax;
-mod uri;
mod workspace;
pub use self::{
capabilities::ClientCapabilitiesExt,
- context::ServerContext,
+ document::*,
+ environment::Environment,
label::*,
lang_data::*,
language::DocumentLanguage,
@@ -33,6 +32,5 @@ pub use self::{
options::*,
range::RangeExt,
server::Server,
- uri::Uri,
- workspace::*,
+ workspace::{Workspace, WorkspaceEvent},
};
diff --git a/support/texlab/src/line_index.rs b/support/texlab/src/line_index.rs
index 3b36cd6802..70e8f8128b 100644
--- a/support/texlab/src/line_index.rs
+++ b/support/texlab/src/line_index.rs
@@ -4,7 +4,7 @@
//! representation.
use std::iter;
-use cstree::{TextRange, TextSize};
+use rowan::{TextRange, TextSize};
use rustc_hash::FxHashMap;
#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/support/texlab/src/line_index_ext.rs b/support/texlab/src/line_index_ext.rs
index 8c8f83f7b4..9faccf1c86 100644
--- a/support/texlab/src/line_index_ext.rs
+++ b/support/texlab/src/line_index_ext.rs
@@ -1,5 +1,5 @@
-use cstree::{TextRange, TextSize};
use lsp_types::{Position, Range};
+use rowan::{TextRange, TextSize};
use crate::{LineColUtf16, LineIndex};
diff --git a/support/texlab/src/main.rs b/support/texlab/src/main.rs
index 415de3b804..c3616a3e96 100644
--- a/support/texlab/src/main.rs
+++ b/support/texlab/src/main.rs
@@ -1,29 +1,34 @@
use std::{env, fs::OpenOptions, io, path::PathBuf};
use anyhow::Result;
+use clap::Parser;
use log::LevelFilter;
use lsp_server::Connection;
-use structopt::StructOpt;
use texlab::Server;
/// An implementation of the Language Server Protocol for LaTeX
-#[derive(Debug, StructOpt)]
+#[derive(Debug, Parser)]
+#[clap(version)]
struct Opts {
/// Increase message verbosity (-vvvv for max verbosity)
- #[structopt(short, long, parse(from_occurrences))]
+ #[clap(short, long, parse(from_occurrences))]
verbosity: u8,
/// No output printed to stderr
- #[structopt(short, long)]
+ #[clap(short, long)]
quiet: bool,
/// Write the logging output to FILE
- #[structopt(long, name = "FILE", parse(from_os_str))]
+ #[clap(long, name = "FILE", parse(from_os_str))]
log_file: Option<PathBuf>,
+
+ /// Print version information and exit
+ #[clap(short = 'V', long)]
+ version: bool,
}
fn main() -> Result<()> {
- let opts = Opts::from_args();
+ let opts = Opts::parse();
setup_logger(opts);
let (connection, threads) = Connection::stdio();
diff --git a/support/texlab/src/options.rs b/support/texlab/src/options.rs
index 20f92a90f4..211b3468c4 100644
--- a/support/texlab/src/options.rs
+++ b/support/texlab/src/options.rs
@@ -28,7 +28,8 @@ pub struct Options {
#[serde(default)]
pub latexindent: LatexindentOptions,
- pub forward_search: Option<ForwardSearchOptions>,
+ #[serde(default)]
+ pub forward_search: ForwardSearchOptions,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
diff --git a/support/texlab/src/req_queue.rs b/support/texlab/src/req_queue.rs
index 3fb875a8be..c1eb7b9594 100644
--- a/support/texlab/src/req_queue.rs
+++ b/support/texlab/src/req_queue.rs
@@ -1,10 +1,7 @@
-use cancellation::CancellationTokenSource;
use crossbeam_channel::Sender;
use lsp_server::ResponseError;
-pub struct IncomingData {
- pub(crate) token_source: CancellationTokenSource,
-}
+pub struct IncomingData;
pub struct OutgoingData {
pub(crate) sender: Sender<Result<serde_json::Value, ResponseError>>,
diff --git a/support/texlab/src/server.rs b/support/texlab/src/server.rs
index 2521faba09..5f833b65bb 100644
--- a/support/texlab/src/server.rs
+++ b/support/texlab/src/server.rs
@@ -4,35 +4,15 @@ use std::{
};
use anyhow::Result;
-use cancellation::{CancellationToken, CancellationTokenSource};
-use crossbeam_channel::Sender;
+use crossbeam_channel::{Receiver, Sender};
use log::{error, info, warn};
-use lsp_server::{Connection, ErrorCode, Message, RequestId};
-use lsp_types::{
- notification::{
- Cancel, DidChangeConfiguration, DidChangeTextDocument, DidOpenTextDocument,
- DidSaveTextDocument, PublishDiagnostics,
- },
- request::{
- DocumentLinkRequest, FoldingRangeRequest, Formatting, GotoDefinition, PrepareRenameRequest,
- References, Rename, SemanticTokensRangeRequest,
- },
- *,
-};
-use notification::DidCloseTextDocument;
-use notify::RecursiveMode;
-use request::{
- Completion, DocumentHighlightRequest, DocumentSymbolRequest, HoverRequest,
- ResolveCompletionItem, WorkspaceSymbol,
-};
+use lsp_server::{Connection, Message, RequestId};
+use lsp_types::{notification::*, request::*, *};
use serde::Serialize;
use threadpool::ThreadPool;
use crate::{
- client::send_notification,
- component_db::COMPONENT_DATABASE,
- config::{pull_config, push_config, register_config_capability},
- create_workspace_full,
+ client::{send_notification, send_request},
diagnostics::{DiagnosticsDebouncer, DiagnosticsManager, DiagnosticsMessage},
dispatch::{NotificationDispatcher, RequestDispatcher},
distro::Distribution,
@@ -42,19 +22,27 @@ use crate::{
prepare_rename_all, rename_all, BuildEngine, BuildParams, BuildResult, BuildStatus,
FeatureRequest, ForwardSearchResult, ForwardSearchStatus,
},
- req_queue::{self, IncomingData, ReqQueue},
- ClientCapabilitiesExt, Document, DocumentLanguage, LineIndexExt, ServerContext, Uri, Workspace,
- WorkspaceSource,
+ req_queue::{IncomingData, ReqQueue},
+ ClientCapabilitiesExt, DocumentLanguage, Environment, LineIndex, LineIndexExt, Options,
+ Workspace, WorkspaceEvent,
};
+#[derive(Debug)]
+enum InternalMessage {
+ SetDistro(Distribution),
+ SetOptions(Options),
+}
+
+#[derive(Clone)]
pub struct Server {
- connection: Connection,
- context: Arc<ServerContext>,
+ connection: Arc<Connection>,
+ internal_tx: Sender<InternalMessage>,
+ internal_rx: Receiver<InternalMessage>,
req_queue: Arc<Mutex<ReqQueue>>,
- workspace: Arc<dyn Workspace>,
- static_debouncer: DiagnosticsDebouncer,
- chktex_debouncer: DiagnosticsDebouncer,
- pool: ThreadPool,
+ workspace: Workspace,
+ static_debouncer: Arc<DiagnosticsDebouncer>,
+ chktex_debouncer: Arc<DiagnosticsDebouncer>,
+ pool: Arc<Mutex<ThreadPool>>,
load_resolver: bool,
build_engine: Arc<BuildEngine>,
}
@@ -65,30 +53,38 @@ impl Server {
current_dir: PathBuf,
load_resolver: bool,
) -> Result<Self> {
- let context = Arc::new(ServerContext::new(current_dir));
let req_queue = Arc::default();
- let workspace = Arc::new(create_workspace_full(Arc::clone(&context))?);
+ let workspace = Workspace::new(Environment::new(Arc::new(current_dir)));
let diag_manager = Arc::new(Mutex::new(DiagnosticsManager::default()));
- let static_debouncer =
- create_static_debouncer(Arc::clone(&diag_manager), &connection, Arc::clone(&context));
+ let static_debouncer = Arc::new(create_static_debouncer(
+ Arc::clone(&diag_manager),
+ &connection,
+ ));
- let chktex_debouncer =
- create_chktex_debouncer(diag_manager, &connection, Arc::clone(&context));
+ let chktex_debouncer = Arc::new(create_chktex_debouncer(diag_manager, &connection));
+
+ let (internal_tx, internal_rx) = crossbeam_channel::unbounded();
Ok(Self {
- connection,
- context,
+ connection: Arc::new(connection),
+ internal_tx,
+ internal_rx,
req_queue,
workspace,
static_debouncer,
chktex_debouncer,
- pool: threadpool::Builder::new().build(),
+ pool: Arc::new(Mutex::new(threadpool::Builder::new().build())),
load_resolver,
build_engine: Arc::default(),
})
}
+ fn spawn(&self, job: impl FnOnce(Self) + Send + 'static) {
+ let server = self.clone();
+ self.pool.lock().unwrap().execute(move || job(server));
+ }
+
fn capabilities(&self) -> ServerCapabilities {
ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Options(
@@ -131,18 +127,6 @@ impl Server {
})),
document_highlight_provider: Some(OneOf::Left(true)),
document_formatting_provider: Some(OneOf::Left(true)),
- #[cfg(feature = "semantic")]
- semantic_tokens_provider: Some(
- SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions {
- full: None,
- range: Some(true),
- legend: SemanticTokensLegend {
- token_types: crate::features::legend::SUPPORTED_TYPES.to_vec(),
- token_modifiers: crate::features::legend::SUPPORTED_MODIFIERS.to_vec(),
- },
- work_done_progress_options: WorkDoneProgressOptions::default(),
- }),
- ),
..ServerCapabilities::default()
}
}
@@ -151,8 +135,8 @@ impl Server {
let (id, params) = self.connection.initialize_start()?;
let params: InitializeParams = serde_json::from_value(params)?;
- *self.context.client_capabilities.lock().unwrap() = params.capabilities;
- *self.context.client_info.lock().unwrap() = params.client_info;
+ self.workspace.environment.client_capabilities = Arc::new(params.capabilities);
+ self.workspace.environment.client_info = params.client_info.map(Arc::new);
let result = InitializeResult {
capabilities: self.capabilities(),
@@ -164,49 +148,162 @@ impl Server {
self.connection
.initialize_finish(id, serde_json::to_value(result)?)?;
- let cx = Arc::clone(&self.context);
if self.load_resolver {
- self.pool.execute(move || {
+ self.spawn(move |server| {
let distro = Distribution::detect();
info!("Detected distribution: {}", distro.kind);
- *cx.resolver.lock().unwrap() = distro.resolver;
+
+ server
+ .internal_tx
+ .send(InternalMessage::SetDistro(distro))
+ .unwrap();
});
}
self.register_diagnostics_handler();
- let req_queue = Arc::clone(&self.req_queue);
- let sender = self.connection.sender.clone();
- let context = Arc::clone(&self.context);
- let workspace = Arc::clone(&self.workspace);
- self.pool.execute(move || {
- register_config_capability(&req_queue, &sender, &context.client_capabilities);
- pull_and_reparse_all(req_queue, sender, context, workspace);
+ self.spawn(move |server| {
+ server.register_config_capability();
+ server.register_file_watching();
+ server.pull_config();
});
Ok(())
}
+ fn register_file_watching(&self) {
+ if self
+ .workspace
+ .environment
+ .client_capabilities
+ .has_file_watching_support()
+ {
+ let options = DidChangeWatchedFilesRegistrationOptions {
+ watchers: vec![FileSystemWatcher {
+ glob_pattern: "**/*.{aux,log}".into(),
+ kind: Some(WatchKind::Create | WatchKind::Change | WatchKind::Delete),
+ }],
+ };
+
+ let reg = Registration {
+ id: "build-watch".to_string(),
+ method: DidChangeWatchedFiles::METHOD.to_string(),
+ register_options: Some(serde_json::to_value(options).unwrap()),
+ };
+
+ let params = RegistrationParams {
+ registrations: vec![reg],
+ };
+
+ if let Err(why) =
+ send_request::<RegisterCapability>(&self.req_queue, &self.connection.sender, params)
+ {
+ error!(
+ "Failed to register \"{}\" notification: {}",
+ DidChangeWatchedFiles::METHOD,
+ why
+ );
+ }
+ }
+ }
+
+ fn register_config_capability(&self) {
+ if self
+ .workspace
+ .environment
+ .client_capabilities
+ .has_push_configuration_support()
+ {
+ let reg = Registration {
+ id: "pull-config".to_string(),
+ method: DidChangeConfiguration::METHOD.to_string(),
+ register_options: None,
+ };
+
+ let params = RegistrationParams {
+ registrations: vec![reg],
+ };
+
+ if let Err(why) =
+ send_request::<RegisterCapability>(&self.req_queue, &self.connection.sender, params)
+ {
+ error!(
+ "Failed to register \"{}\" notification: {}",
+ DidChangeConfiguration::METHOD,
+ why
+ );
+ }
+ }
+ }
+
fn register_diagnostics_handler(&mut self) {
- let sender = self.static_debouncer.sender.clone();
- self.workspace
- .register_open_handler(Arc::new(move |workspace, document| {
- let message = DiagnosticsMessage::Analyze {
- workspace,
- document,
+ let (event_sender, event_receiver) = crossbeam_channel::unbounded();
+ let diag_sender = self.static_debouncer.sender.clone();
+ std::thread::spawn(move || {
+ for event in event_receiver {
+ match event {
+ WorkspaceEvent::Changed(workspace, document) => {
+ let message = DiagnosticsMessage::Analyze {
+ workspace,
+ document,
+ };
+
+ if diag_sender.send(message).is_err() {
+ break;
+ }
+ }
};
- sender.send(message).unwrap();
- }));
+ }
+ });
+
+ self.workspace.listeners.push(event_sender);
}
- fn register_incoming_request(&self, id: RequestId) -> Arc<CancellationToken> {
- let token_source = CancellationTokenSource::new();
- let token = Arc::clone(token_source.token());
+ fn register_incoming_request(&self, id: RequestId) {
let mut req_queue = self.req_queue.lock().unwrap();
- req_queue
- .incoming
- .register(id.clone(), IncomingData { token_source });
- token
+ req_queue.incoming.register(id, IncomingData);
+ }
+
+ fn pull_config(&self) {
+ if !self
+ .workspace
+ .environment
+ .client_capabilities
+ .has_pull_configuration_support()
+ {
+ return;
+ }
+
+ let params = ConfigurationParams {
+ items: vec![ConfigurationItem {
+ section: Some("texlab".to_string()),
+ scope_uri: None,
+ }],
+ };
+
+ match send_request::<WorkspaceConfiguration>(
+ &self.req_queue,
+ &self.connection.sender,
+ params,
+ ) {
+ Ok(mut json) => {
+ let value = json.pop().expect("invalid configuration request");
+ let options = match serde_json::from_value(value) {
+ Ok(new_options) => new_options,
+ Err(why) => {
+ warn!("Invalid configuration section \"texlab\": {}", why);
+ Options::default()
+ }
+ };
+
+ self.internal_tx
+ .send(InternalMessage::SetOptions(options))
+ .unwrap();
+ }
+ Err(why) => {
+ error!("Retrieving configuration failed: {}", why);
+ }
+ };
}
fn cancel(&self, params: CancelParams) -> Result<()> {
@@ -216,165 +313,146 @@ impl Server {
};
let mut req_queue = self.req_queue.lock().unwrap();
- if let Some(data) = req_queue.incoming.complete(id.clone()) {
- data.token_source.cancel();
+ req_queue.incoming.complete(id);
+
+ Ok(())
+ }
+
+ fn did_change_watched_files(&mut self, params: DidChangeWatchedFilesParams) -> Result<()> {
+ for change in params.changes {
+ if let Ok(path) = change.uri.to_file_path() {
+ match change.typ {
+ FileChangeType::CREATED | FileChangeType::CHANGED => {
+ self.workspace.reload(path)?;
+ }
+ FileChangeType::DELETED => {
+ self.workspace.documents_by_uri.remove(&change.uri);
+ }
+ _ => {}
+ }
+ }
}
Ok(())
}
- fn did_change_configuration(&self, params: DidChangeConfigurationParams) -> Result<()> {
- let client_capabilities = { self.context.client_capabilities.lock().unwrap().clone() };
- if client_capabilities.has_pull_configuration_support() {
- let req_queue = Arc::clone(&self.req_queue);
- let sender = self.connection.sender.clone();
- let context = Arc::clone(&self.context);
- let workspace = Arc::clone(&self.workspace);
- self.pool.execute(move || {
- pull_and_reparse_all(req_queue, sender, context, workspace);
+ fn did_change_configuration(&mut self, params: DidChangeConfigurationParams) -> Result<()> {
+ if self
+ .workspace
+ .environment
+ .client_capabilities
+ .has_pull_configuration_support()
+ {
+ self.spawn(move |server| {
+ server.pull_config();
});
} else {
- push_config(&self.context.options, params.settings);
- if let Some(path) = { self.context.options.read().unwrap().aux_directory.clone() } {
- let _ = self.workspace.watch(path, RecursiveMode::NonRecursive);
- }
+ match serde_json::from_value(params.settings) {
+ Ok(options) => {
+ self.workspace.environment.options = Arc::new(options);
+ }
+ Err(why) => {
+ error!("Invalid configuration: {}", why);
+ }
+ };
- let workspace = Arc::clone(&self.workspace);
- self.pool.execute(move || {
- reparse_all(workspace.as_ref());
- });
+ self.reparse_all()?;
}
Ok(())
}
- fn did_open(&self, params: DidOpenTextDocumentParams) -> Result<()> {
+ fn did_open(&mut self, params: DidOpenTextDocumentParams) -> Result<()> {
let language_id = &params.text_document.language_id;
let language = DocumentLanguage::by_language_id(language_id);
let document = self.workspace.open(
- Arc::new(params.text_document.uri.into()),
- params.text_document.text,
+ Arc::new(params.text_document.uri),
+ Arc::new(params.text_document.text),
language.unwrap_or(DocumentLanguage::Latex),
- WorkspaceSource::Client,
- );
-
- let should_lint = { self.context.options.read().unwrap().chktex.on_open_and_save };
- if let Some(document) = self
- .workspace
- .get(document.uri.as_ref())
- .filter(|_| should_lint)
- {
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: Arc::clone(&self.workspace),
- document,
- })?;
- };
- Ok(())
- }
-
- fn did_change(&self, mut params: DidChangeTextDocumentParams) -> Result<()> {
- let uri = params.text_document.uri.into();
- let old_document = self.workspace.get(&uri);
- let old_text = old_document.as_ref().map(|document| document.text.as_str());
- let uri = Arc::new(uri);
+ )?;
- let language = self
- .workspace
- .get(&uri)
- .map(|document| document.data.language())
- .unwrap_or(DocumentLanguage::Latex);
-
- let new_document = match &old_document {
- Some(old_document) => params
- .content_changes
- .into_iter()
- .fold(Arc::clone(&old_document), |old_document, change| {
- self.merge_text_changes(&old_document, language, change)
- }),
- None => self.workspace.open(
- Arc::clone(&uri),
- params.content_changes.pop().unwrap().text,
- language,
- WorkspaceSource::Client,
- ),
- };
+ self.workspace.viewport.insert(Arc::clone(&document.uri));
- let line = match old_text {
- Some(old_text) => old_text
- .lines()
- .zip(new_document.text.lines())
- .position(|(a, b)| a != b)
- .unwrap_or_default() as u32,
- None => 0,
- };
- self.build_engine
- .positions_by_uri
- .insert(Arc::clone(&uri), Position::new(line, 0));
-
- let should_lint = { self.context.options.read().unwrap().chktex.on_edit };
- if should_lint {
+ if self.workspace.environment.options.chktex.on_open_and_save {
self.chktex_debouncer
.sender
.send(DiagnosticsMessage::Analyze {
- workspace: Arc::clone(&self.workspace),
- document: new_document,
+ workspace: self.workspace.clone(),
+ document,
})?;
- };
+ }
Ok(())
}
- fn merge_text_changes(
- &self,
- old_document: &Document,
- new_language: DocumentLanguage,
- change: TextDocumentContentChangeEvent,
- ) -> Arc<Document> {
- let new_text = match change.range {
- Some(range) => {
- let range = old_document.line_index.offset_lsp_range(range);
- let mut new_text = String::new();
- new_text.push_str(&old_document.text[..range.start().into()]);
- new_text.push_str(&change.text);
- new_text.push_str(&old_document.text[range.end().into()..]);
- new_text
+ fn did_change(&mut self, params: DidChangeTextDocumentParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri);
+ match self.workspace.documents_by_uri.get(&uri).cloned() {
+ Some(old_document) => {
+ let mut text = old_document.text.to_string();
+ apply_document_edit(&mut text, params.content_changes);
+ let language = old_document.data.language();
+ let new_document =
+ self.workspace
+ .open(Arc::clone(&uri), Arc::new(text), language)?;
+ self.workspace
+ .viewport
+ .insert(Arc::clone(&new_document.uri));
+
+ self.build_engine.positions_by_uri.insert(
+ Arc::clone(&uri),
+ Position::new(
+ old_document
+ .text
+ .lines()
+ .zip(new_document.text.lines())
+ .position(|(a, b)| a != b)
+ .unwrap_or_default() as u32,
+ 0,
+ ),
+ );
+
+ if self.workspace.environment.options.chktex.on_edit {
+ self.chktex_debouncer
+ .sender
+ .send(DiagnosticsMessage::Analyze {
+ workspace: self.workspace.clone(),
+ document: new_document,
+ })?;
+ };
}
- None => change.text,
+ None => match uri.to_file_path() {
+ Ok(path) => {
+ self.workspace.load(path)?;
+ }
+ Err(_) => return Ok(()),
+ },
};
- self.workspace.open(
- Arc::clone(&old_document.uri),
- new_text,
- new_language,
- WorkspaceSource::Client,
- )
+ Ok(())
}
fn did_save(&self, params: DidSaveTextDocumentParams) -> Result<()> {
- let uri = params.text_document.uri.into();
-
- let should_build = { self.context.options.read().unwrap().build.on_save };
- if let Some(request) =
- self.workspace
- .get(&uri)
- .filter(|_| should_build)
- .and_then(|document| {
- self.feature_request(
- Arc::clone(&document.uri),
- BuildParams {
- text_document: TextDocumentIdentifier::new(uri.clone().into()),
- },
- )
- })
+ let uri = params.text_document.uri;
+
+ if let Some(request) = self
+ .workspace
+ .documents_by_uri
+ .get(&uri)
+ .filter(|_| self.workspace.environment.options.build.on_save)
+ .map(|document| {
+ self.feature_request(
+ Arc::clone(&document.uri),
+ BuildParams {
+ text_document: TextDocumentIdentifier::new(uri.clone()),
+ },
+ )
+ })
{
- let lsp_sender = self.connection.sender.clone();
- let req_queue = Arc::clone(&self.req_queue);
- let build_engine = Arc::clone(&self.build_engine);
- self.pool.execute(move || {
- build_engine
- .build(request, CancellationToken::none(), &req_queue, &lsp_sender)
+ self.spawn(move |server| {
+ server
+ .build_engine
+ .build(request, &server.req_queue, &server.connection.sender)
.unwrap_or_else(|why| {
error!("Build failed: {}", why);
BuildResult {
@@ -384,357 +462,228 @@ impl Server {
});
}
- let should_lint = { self.context.options.read().unwrap().chktex.on_open_and_save };
- if let Some(document) = self.workspace.get(&uri).filter(|_| should_lint) {
+ if let Some(document) = self
+ .workspace
+ .documents_by_uri
+ .get(&uri)
+ .filter(|_| self.workspace.environment.options.chktex.on_open_and_save)
+ .cloned()
+ {
self.chktex_debouncer
.sender
.send(DiagnosticsMessage::Analyze {
- workspace: Arc::clone(&self.workspace),
+ workspace: self.workspace.clone(),
document,
})?;
};
Ok(())
}
- fn did_close(&self, params: DidCloseTextDocumentParams) -> Result<()> {
- let uri = params.text_document.uri.into();
- self.workspace.close(&uri);
+ fn did_close(&mut self, params: DidCloseTextDocumentParams) -> Result<()> {
+ self.workspace.close(&params.text_document.uri);
Ok(())
}
- fn feature_request<P>(&self, uri: Arc<Uri>, params: P) -> Option<FeatureRequest<P>> {
- Some(FeatureRequest {
- context: Arc::clone(&self.context),
+ fn feature_request<P>(&self, uri: Arc<Url>, params: P) -> FeatureRequest<P> {
+ FeatureRequest {
params,
- workspace: Arc::clone(&self.workspace),
- subset: self.workspace.subset(uri)?,
- })
- }
-
- fn send_feature_error(&self, id: RequestId) -> Result<()> {
- let resp = lsp_server::Response::new_err(
- id,
- ErrorCode::InternalError as i32,
- "unknown document URI".to_string(),
- );
- self.connection.sender.send(resp.into())?;
- Ok(())
+ workspace: self.workspace.slice(&uri),
+ uri,
+ }
}
fn handle_feature_request<P, R, H>(
&self,
id: RequestId,
params: P,
- uri: Arc<Uri>,
- token: &Arc<CancellationToken>,
+ uri: Arc<Url>,
handler: H,
) -> Result<()>
where
P: Send + 'static,
R: Serialize,
- H: FnOnce(FeatureRequest<P>, &CancellationToken) -> R + Send + 'static,
+ H: FnOnce(FeatureRequest<P>) -> R + Send + 'static,
{
- match self.feature_request(uri, params) {
- Some(req) => {
- let sender = self.connection.sender.clone();
- let token = Arc::clone(token);
- self.pool.execute(move || {
- let result = handler(req, &token);
- if token.is_canceled() {
- sender.send(cancel_response(id).into()).unwrap();
- } else {
- sender
- .send(lsp_server::Response::new_ok(id, result).into())
- .unwrap();
- }
- });
- }
- None => {
- self.send_feature_error(id)?;
- }
- };
+ self.spawn(move |server| {
+ let request = server.feature_request(uri, params);
+ let result = handler(request);
+ server
+ .connection
+ .sender
+ .send(lsp_server::Response::new_ok(id, result).into())
+ .unwrap();
+ });
+
Ok(())
}
- fn document_link(
- &self,
- id: RequestId,
- params: DocumentLinkParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, find_document_links)?;
+ fn document_link(&self, id: RequestId, params: DocumentLinkParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, find_document_links)?;
Ok(())
}
- fn document_symbols(
- &self,
- id: RequestId,
- params: DocumentSymbolParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, find_document_symbols)?;
+ fn document_symbols(&self, id: RequestId, params: DocumentSymbolParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, find_document_symbols)?;
Ok(())
}
- fn workspace_symbols(
- &self,
- id: RequestId,
- params: WorkspaceSymbolParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let sender = self.connection.sender.clone();
- let workspace = Arc::clone(&self.workspace);
- let token = Arc::clone(token);
- self.pool.execute(move || {
- let result = find_workspace_symbols(workspace.as_ref(), &params, &token);
- if token.is_canceled() {
- sender.send(cancel_response(id).into()).unwrap();
- } else {
- sender
- .send(lsp_server::Response::new_ok(id, result).into())
- .unwrap();
- }
+ fn workspace_symbols(&self, id: RequestId, params: WorkspaceSymbolParams) -> Result<()> {
+ self.spawn(move |server| {
+ let result = find_workspace_symbols(&server.workspace, &params);
+ server
+ .connection
+ .sender
+ .send(lsp_server::Response::new_ok(id, result).into())
+ .unwrap();
});
Ok(())
}
#[cfg(feature = "completion")]
- fn completion(
- &self,
- id: RequestId,
- params: CompletionParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(
- params
- .text_document_position
- .text_document
- .uri
- .clone()
- .into(),
- );
+ fn completion(&self, id: RequestId, params: CompletionParams) -> Result<()> {
+ let uri = Arc::new(params.text_document_position.text_document.uri.clone());
self.build_engine
.positions_by_uri
.insert(Arc::clone(&uri), params.text_document_position.position);
- self.handle_feature_request(id, params, uri, token, crate::features::complete)?;
+ self.handle_feature_request(id, params, uri, crate::features::complete)?;
Ok(())
}
#[cfg(feature = "completion")]
- fn completion_resolve(
- &self,
- id: RequestId,
- mut item: CompletionItem,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let sender = self.connection.sender.clone();
- let token = Arc::clone(token);
- let workspace = Arc::clone(&self.workspace);
- self.pool.execute(move || {
+ fn completion_resolve(&self, id: RequestId, mut item: CompletionItem) -> Result<()> {
+ use rowan::ast::AstNode;
+
+ use crate::{
+ citation, component_db::COMPONENT_DATABASE, features::CompletionItemData,
+ syntax::bibtex,
+ };
+
+ self.spawn(move |server| {
match serde_json::from_value(item.data.clone().unwrap()).unwrap() {
- crate::features::CompletionItemData::Package
- | crate::features::CompletionItemData::Class => {
+ CompletionItemData::Package | CompletionItemData::Class => {
item.documentation = COMPONENT_DATABASE
.documentation(&item.label)
.map(Documentation::MarkupContent);
}
- #[cfg(feature = "citation")]
- crate::features::CompletionItemData::Citation { uri, key } => {
- if let Some(document) = workspace.get(&uri) {
+ CompletionItemData::Citation { uri, key } => {
+ if let Some(document) = server.workspace.documents_by_uri.get(&uri) {
if let Some(data) = document.data.as_bibtex() {
- let markup = crate::citation::render_citation(&data.root, &key);
- item.documentation = markup.map(Documentation::MarkupContent);
+ let root = bibtex::SyntaxNode::new_root(data.green.clone());
+ item.documentation = bibtex::Root::cast(root)
+ .and_then(|root| root.find_entry(&key))
+ .and_then(|entry| citation::render(&entry))
+ .map(|value| {
+ Documentation::MarkupContent(MarkupContent {
+ kind: MarkupKind::Markdown,
+ value,
+ })
+ });
}
}
}
_ => {}
};
- drop(workspace);
- if token.is_canceled() {
- sender.send(cancel_response(id).into()).unwrap();
- } else {
- sender
- .send(lsp_server::Response::new_ok(id, item).into())
- .unwrap();
- }
+ server
+ .connection
+ .sender
+ .send(lsp_server::Response::new_ok(id, item).into())
+ .unwrap();
});
Ok(())
}
- fn folding_range(
- &self,
- id: RequestId,
- params: FoldingRangeParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, find_foldings)?;
+ fn folding_range(&self, id: RequestId, params: FoldingRangeParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, find_foldings)?;
Ok(())
}
- fn references(
- &self,
- id: RequestId,
- params: ReferenceParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(
- params
- .text_document_position
- .text_document
- .uri
- .clone()
- .into(),
- );
- self.handle_feature_request(id, params, uri, token, find_all_references)?;
+ fn references(&self, id: RequestId, params: ReferenceParams) -> Result<()> {
+ let uri = Arc::new(params.text_document_position.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, find_all_references)?;
Ok(())
}
- fn hover(
- &self,
- id: RequestId,
- params: HoverParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
+ fn hover(&self, id: RequestId, params: HoverParams) -> Result<()> {
let uri = Arc::new(
params
.text_document_position_params
.text_document
.uri
- .clone()
- .into(),
+ .clone(),
);
self.build_engine.positions_by_uri.insert(
Arc::clone(&uri),
params.text_document_position_params.position,
);
- self.handle_feature_request(id, params, uri, token, find_hover)?;
+ self.handle_feature_request(id, params, uri, find_hover)?;
Ok(())
}
- fn goto_definition(
- &self,
- id: RequestId,
- params: GotoDefinitionParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
+ fn goto_definition(&self, id: RequestId, params: GotoDefinitionParams) -> Result<()> {
let uri = Arc::new(
params
.text_document_position_params
.text_document
.uri
- .clone()
- .into(),
+ .clone(),
);
- self.handle_feature_request(id, params, uri, token, goto_definition)?;
+ self.handle_feature_request(id, params, uri, goto_definition)?;
Ok(())
}
- fn prepare_rename(
- &self,
- id: RequestId,
- params: TextDocumentPositionParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, prepare_rename_all)?;
+ fn prepare_rename(&self, id: RequestId, params: TextDocumentPositionParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, prepare_rename_all)?;
Ok(())
}
- fn rename(
- &self,
- id: RequestId,
- params: RenameParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(
- params
- .text_document_position
- .text_document
- .uri
- .clone()
- .into(),
- );
- self.handle_feature_request(id, params, uri, token, rename_all)?;
+ fn rename(&self, id: RequestId, params: RenameParams) -> Result<()> {
+ let uri = Arc::new(params.text_document_position.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, rename_all)?;
Ok(())
}
- fn document_highlight(
- &self,
- id: RequestId,
- params: DocumentHighlightParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
+ fn document_highlight(&self, id: RequestId, params: DocumentHighlightParams) -> Result<()> {
let uri = Arc::new(
params
.text_document_position_params
.text_document
.uri
- .clone()
- .into(),
+ .clone(),
);
- self.handle_feature_request(id, params, uri, token, find_document_highlights)?;
+ self.handle_feature_request(id, params, uri, find_document_highlights)?;
Ok(())
}
- fn formatting(
- &self,
- id: RequestId,
- params: DocumentFormattingParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, format_source_code)?;
+ fn formatting(&self, id: RequestId, params: DocumentFormattingParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, format_source_code)?;
Ok(())
}
- #[cfg(feature = "semantic")]
- fn semantic_tokens_range(
- &self,
- id: RequestId,
- params: SemanticTokensRangeParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(
- id,
- params,
- uri,
- token,
- crate::features::find_semantic_tokens_range,
- )?;
- Ok(())
- }
-
- #[cfg(not(feature = "semantic"))]
fn semantic_tokens_range(
&self,
_id: RequestId,
_params: SemanticTokensRangeParams,
- _token: &Arc<CancellationToken>,
) -> Result<()> {
Ok(())
}
- fn build(
- &self,
- id: RequestId,
- params: BuildParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
+ fn build(&self, id: RequestId, params: BuildParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
let lsp_sender = self.connection.sender.clone();
let req_queue = Arc::clone(&self.req_queue);
let build_engine = Arc::clone(&self.build_engine);
- self.handle_feature_request(id, params, uri, token, move |request, token| {
+ self.handle_feature_request(id, params, uri, move |request| {
build_engine
- .build(request, token, &req_queue, &lsp_sender)
+ .build(request, &req_queue, &lsp_sender)
.unwrap_or_else(|why| {
error!("Build failed: {}", why);
BuildResult {
@@ -745,104 +694,126 @@ impl Server {
Ok(())
}
- fn forward_search(
- &self,
- id: RequestId,
- params: TextDocumentPositionParams,
- token: &Arc<CancellationToken>,
- ) -> Result<()> {
- let uri = Arc::new(params.text_document.uri.clone().into());
- self.handle_feature_request(id, params, uri, token, |req, token| {
- crate::features::execute_forward_search(req, token).unwrap_or_else(|| {
- ForwardSearchResult {
- status: ForwardSearchStatus::ERROR,
- }
+ fn forward_search(&self, id: RequestId, params: TextDocumentPositionParams) -> Result<()> {
+ let uri = Arc::new(params.text_document.uri.clone());
+ self.handle_feature_request(id, params, uri, |req| {
+ crate::features::execute_forward_search(req).unwrap_or(ForwardSearchResult {
+ status: ForwardSearchStatus::ERROR,
})
})?;
Ok(())
}
- fn process_messages(&self) -> Result<()> {
- for msg in &self.connection.receiver {
- match msg {
- Message::Request(request) => {
- if self.connection.handle_shutdown(&request)? {
- return Ok(());
- }
+ fn reparse_all(&mut self) -> Result<()> {
+ for document in self
+ .workspace
+ .documents_by_uri
+ .values()
+ .cloned()
+ .collect::<Vec<_>>()
+ {
+ self.workspace.open(
+ Arc::clone(&document.uri),
+ document.text.clone(),
+ document.data.language(),
+ )?;
+ }
- let token = self.register_incoming_request(request.id.clone());
- if let Some(response) = RequestDispatcher::new(request)
- .on::<DocumentLinkRequest, _>(|id, params| {
- self.document_link(id, params, &token)
- })?
- .on::<FoldingRangeRequest, _>(|id, params| {
- self.folding_range(id, params, &token)
- })?
- .on::<References, _>(|id, params| self.references(id, params, &token))?
- .on::<HoverRequest, _>(|id, params| self.hover(id, params, &token))?
- .on::<DocumentSymbolRequest, _>(|id, params| {
- self.document_symbols(id, params, &token)
- })?
- .on::<WorkspaceSymbol, _>(|id, params| {
- self.workspace_symbols(id, params, &token)
- })?
- .on::<Completion, _>(|id, params| {
- #[cfg(feature = "completion")]
- self.completion(id, params, &token)?;
- Ok(())
- })?
- .on::<ResolveCompletionItem, _>(|id, params| {
- #[cfg(feature = "completion")]
- self.completion_resolve(id, params, &token)?;
- Ok(())
- })?
- .on::<GotoDefinition, _>(|id, params| {
- self.goto_definition(id, params, &token)
- })?
- .on::<PrepareRenameRequest, _>(|id, params| {
- self.prepare_rename(id, params, &token)
- })?
- .on::<Rename, _>(|id, params| self.rename(id, params, &token))?
- .on::<DocumentHighlightRequest, _>(|id, params| {
- self.document_highlight(id, params, &token)
- })?
- .on::<Formatting, _>(|id, params| self.formatting(id, params, &token))?
- .on::<BuildRequest, _>(|id, params| self.build(id, params, &token))?
- .on::<ForwardSearchRequest, _>(|id, params| {
- self.forward_search(id, params, &token)
- })?
- .on::<SemanticTokensRangeRequest, _>(|id, params| {
- self.semantic_tokens_range(id, params, &token)
- })?
- .default()
- {
- self.connection.sender.send(response.into())?;
- }
- }
- Message::Notification(notification) => {
- NotificationDispatcher::new(notification)
- .on::<Cancel, _>(|params| self.cancel(params))?
- .on::<DidChangeConfiguration, _>(|params| {
- self.did_change_configuration(params)
- })?
- .on::<DidOpenTextDocument, _>(|params| self.did_open(params))?
- .on::<DidChangeTextDocument, _>(|params| self.did_change(params))?
- .on::<DidSaveTextDocument, _>(|params| self.did_save(params))?
- .on::<DidCloseTextDocument, _>(|params| self.did_close(params))?
- .default();
- }
- Message::Response(response) => {
- let mut req_queue = self.req_queue.lock().unwrap();
- let data = req_queue.outgoing.complete(response.id);
- let result = match response.error {
- Some(error) => Err(error),
- None => Ok(response.result.unwrap_or_default()),
+ Ok(())
+ }
+
+ fn process_messages(&mut self) -> Result<()> {
+ loop {
+ crossbeam_channel::select! {
+ recv(&self.connection.receiver) -> msg => {
+ match msg? {
+ Message::Request(request) => {
+ if self.connection.handle_shutdown(&request)? {
+ return Ok(());
+ }
+
+ self.register_incoming_request(request.id.clone());
+ if let Some(response) = RequestDispatcher::new(request)
+ .on::<DocumentLinkRequest, _>(|id, params| self.document_link(id, params))?
+ .on::<FoldingRangeRequest, _>(|id, params| self.folding_range(id, params))?
+ .on::<References, _>(|id, params| self.references(id, params))?
+ .on::<HoverRequest, _>(|id, params| self.hover(id, params))?
+ .on::<DocumentSymbolRequest, _>(|id, params| {
+ self.document_symbols(id, params)
+ })?
+ .on::<WorkspaceSymbol, _>(|id, params| self.workspace_symbols(id, params))?
+ .on::<Completion, _>(|id, params| {
+ #[cfg(feature = "completion")]
+ self.completion(id, params)?;
+ Ok(())
+ })?
+ .on::<ResolveCompletionItem, _>(|id, params| {
+ #[cfg(feature = "completion")]
+ self.completion_resolve(id, params)?;
+ Ok(())
+ })?
+ .on::<GotoDefinition, _>(|id, params| self.goto_definition(id, params))?
+ .on::<PrepareRenameRequest, _>(|id, params| {
+ self.prepare_rename(id, params)
+ })?
+ .on::<Rename, _>(|id, params| self.rename(id, params))?
+ .on::<DocumentHighlightRequest, _>(|id, params| {
+ self.document_highlight(id, params)
+ })?
+ .on::<Formatting, _>(|id, params| self.formatting(id, params))?
+ .on::<BuildRequest, _>(|id, params| self.build(id, params))?
+ .on::<ForwardSearchRequest, _>(|id, params| {
+ self.forward_search(id, params)
+ })?
+ .on::<SemanticTokensRangeRequest, _>(|id, params| {
+ self.semantic_tokens_range(id, params)
+ })?
+ .default()
+ {
+ self.connection.sender.send(response.into())?;
+ }
+ }
+ Message::Notification(notification) => {
+ NotificationDispatcher::new(notification)
+ .on::<Cancel, _>(|params| self.cancel(params))?
+ .on::<DidChangeConfiguration, _>(|params| {
+ self.did_change_configuration(params)
+ })?
+ .on::<DidChangeWatchedFiles, _>(|params| {
+ self.did_change_watched_files(params)
+ })?
+ .on::<DidOpenTextDocument, _>(|params| self.did_open(params))?
+ .on::<DidChangeTextDocument, _>(|params| self.did_change(params))?
+ .on::<DidSaveTextDocument, _>(|params| self.did_save(params))?
+ .on::<DidCloseTextDocument, _>(|params| self.did_close(params))?
+ .default();
+ }
+ Message::Response(response) => {
+ let mut req_queue = self.req_queue.lock().unwrap();
+ if let Some(data) = req_queue.outgoing.complete(response.id) {
+ let result = match response.error {
+ Some(error) => Err(error),
+ None => Ok(response.result.unwrap_or_default()),
+ };
+ data.sender.send(result)?;
+ }
+ }
+ };
+ },
+ recv(&self.internal_rx) -> msg => {
+ match msg? {
+ InternalMessage::SetDistro(distro) => {
+ self.workspace.environment.resolver = Arc::new(distro.resolver);
+ self.reparse_all()?;
+ }
+ InternalMessage::SetOptions(options) => {
+ self.workspace.environment.options = Arc::new(options);
+ self.reparse_all()?;
+ }
};
- data.sender.send(result)?;
}
- }
+ };
}
- Ok(())
}
pub fn run(mut self) -> Result<()> {
@@ -850,47 +821,20 @@ impl Server {
self.process_messages()?;
drop(self.static_debouncer);
drop(self.chktex_debouncer);
- self.pool.join();
+ self.pool.lock().unwrap().join();
Ok(())
}
}
-fn pull_and_reparse_all(
- req_queue: Arc<Mutex<lsp_server::ReqQueue<IncomingData, req_queue::OutgoingData>>>,
- sender: Sender<Message>,
- context: Arc<ServerContext>,
- workspace: Arc<dyn Workspace>,
-) {
- let client_capabilities = { context.client_capabilities.lock().unwrap().clone() };
- pull_config(&req_queue, &sender, &context.options, &client_capabilities);
- if let Some(path) = { context.options.read().unwrap().aux_directory.clone() } {
- let _ = workspace.watch(path, RecursiveMode::NonRecursive);
- }
-
- reparse_all(workspace.as_ref());
-}
-
-fn reparse_all(workspace: &dyn Workspace) {
- for document in workspace.documents() {
- workspace.open(
- Arc::clone(&document.uri),
- document.text.clone(),
- document.language(),
- WorkspaceSource::Client,
- );
- }
-}
-
fn create_static_debouncer(
manager: Arc<Mutex<DiagnosticsManager>>,
conn: &Connection,
- context: Arc<ServerContext>,
) -> DiagnosticsDebouncer {
let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(context, move |workspace, document| {
+ DiagnosticsDebouncer::launch(move |workspace, document| {
let mut manager = manager.lock().unwrap();
- manager.update_static(workspace.as_ref(), Arc::clone(&document.uri));
- if let Err(why) = publish_diagnostics(&sender, workspace.as_ref(), &manager) {
+ manager.update_static(&workspace, Arc::clone(&document.uri));
+ if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
warn!("Failed to publish diagnostics: {}", why);
}
})
@@ -899,14 +843,16 @@ fn create_static_debouncer(
fn create_chktex_debouncer(
manager: Arc<Mutex<DiagnosticsManager>>,
conn: &Connection,
- context: Arc<ServerContext>,
) -> DiagnosticsDebouncer {
let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(Arc::clone(&context), move |workspace, document| {
- let options = { context.options.read().unwrap().clone() };
+ DiagnosticsDebouncer::launch(move |workspace, document| {
let mut manager = manager.lock().unwrap();
- manager.update_chktex(workspace.as_ref(), Arc::clone(&document.uri), &options);
- if let Err(why) = publish_diagnostics(&sender, workspace.as_ref(), &manager) {
+ manager.update_chktex(
+ &workspace,
+ Arc::clone(&document.uri),
+ &workspace.environment.options,
+ );
+ if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
warn!("Failed to publish diagnostics: {}", why);
}
})
@@ -914,15 +860,15 @@ fn create_chktex_debouncer(
fn publish_diagnostics(
sender: &Sender<lsp_server::Message>,
- workspace: &dyn Workspace,
+ workspace: &Workspace,
diag_manager: &DiagnosticsManager,
) -> Result<()> {
- for document in workspace.documents() {
+ for document in workspace.documents_by_uri.values() {
let diagnostics = diag_manager.publish(Arc::clone(&document.uri));
send_notification::<PublishDiagnostics>(
sender,
PublishDiagnosticsParams {
- uri: document.uri.as_ref().clone().into(),
+ uri: document.uri.as_ref().clone(),
version: None,
diagnostics,
},
@@ -931,12 +877,19 @@ fn publish_diagnostics(
Ok(())
}
-fn cancel_response(id: RequestId) -> lsp_server::Response {
- lsp_server::Response::new_err(
- id,
- ErrorCode::RequestCanceled as i32,
- "canceled by client".to_string(),
- )
+fn apply_document_edit(old_text: &mut String, changes: Vec<TextDocumentContentChangeEvent>) {
+ for change in changes {
+ let line_index = LineIndex::new(old_text);
+ match change.range {
+ Some(range) => {
+ let range = std::ops::Range::<usize>::from(line_index.offset_lsp_range(range));
+ old_text.replace_range(range, &change.text);
+ }
+ None => {
+ *old_text = change.text;
+ }
+ };
+ }
}
struct BuildRequest;
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_001.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_001.snap
new file mode 100644
index 0000000000..40b6a6ad00
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_001.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 133
+expression: "render_simple(r#\"\n@incollection{westfahl:space,\n author = {Westfahl, Gary},\n title = {The True Frontier},\n subtitle = {Confronting and Avoiding the Realities of Space in {American}\n Science Fiction Films},\n pages = {55-65},\n crossref = {westfahl:frontier},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {True Frontier, The},\n annotation = {A cross-referenced article from a \\texttt{collection}. This is\n an \\texttt{incollection} entry with a \\texttt{crossref}\n field. Note the \\texttt{subtitle} and \\texttt{indextitle}\n fields},\n}\n\n% booktitle and booksubtitle are only needed for BibTeX's less sophisticated\n% inheritance set-up to make sure westfahl:space shows correctly.\n% With Biber they are not needed.\n@collection{westfahl:frontier,\n editor = {Westfahl, Gary},\n title = {Space and Beyond},\n date = 2000,\n subtitle = {The Frontier Theme in Science Fiction},\n publisher = {Greenwood},\n location = {Westport, Conn. and London},\n langid = {english},\n langidopts = {variant=american},\n booktitle = {Space and Beyond},\n booksubtitle = {The Frontier Theme in Science Fiction},\n annotation = {This is a \\texttt{collection} entry. Note the format of the\n \\texttt{location} field as well as the \\texttt{subtitle}\n field},\n}\"#)"
+---
+Westfahl, Gary. (n.d.). *The True Frontier: Confronting and Avoiding the Realities of Space in American Science Fiction Films*.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_002.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_002.snap
new file mode 100644
index 0000000000..ed55dc0b88
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_002.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 174
+expression: "render_simple(r#\"\n@string{jomch = {J.~Organomet. Chem.}}\n\n@article{aksin,\n author = {Aks{\\i}n, {\\\"O}zge and T{\\\"u}rkmen, Hayati and Artok, Levent\n and {\\c{C}}etinkaya, Bekir and Ni, Chaoying and\n B{\\\"u}y{\\\"u}kg{\\\"u}ng{\\\"o}r, Orhan and {\\\"O}zkal, Erhan},\n title = {Effect of immobilization on catalytic characteristics of\n saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}\n reactions},\n journaltitle = jomch,\n date = 2006,\n volume = 691,\n number = 13,\n pages = {3027-3036},\n indextitle = {Effect of immobilization on catalytic characteristics},\n}\"#)"
+---
+Aksın, Özge, Türkmen, Hayati, Artok, Levent, \\cCetinkaya, Bekir, Ni, Chaoying, Büyükgüngör, Orhan, & Özkal, Erhan. (n.d.). Effect of immobilization on catalytic characteristics of saturated Pd-N-heterocyclic carbenes in Mizoroki-Heck reactions. *J. Organomet. Chem.*, *691*(13), 3027–3036.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_003.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_003.snap
new file mode 100644
index 0000000000..a6455425eb
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_003.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 197
+expression: "render_simple(r#\"\n@article{angenendt,\n author = {Angenendt, Arnold},\n title = {In Honore Salvatoris~-- Vom Sinn und Unsinn der\n Patrozinienkunde},\n journaltitle = {Revue d'Histoire Eccl{\\'e}siastique},\n date = 2002,\n volume = 97,\n pages = {431--456, 791--823},\n langid = {german},\n indextitle = {In Honore Salvatoris},\n shorttitle = {In Honore Salvatoris},\n annotation = {A German article in a French journal. Apart from that, a\n typical \\texttt{article} entry. Note the \\texttt{indextitle}\n field},\n}\"#)"
+---
+Angenendt, Arnold. (n.d.). In Honore Salvatoris – Vom Sinn und Unsinn der Patrozinienkunde. *Revue d’Histoire Ecclésiastique*, *97*, 431–456,.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_004.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_004.snap
new file mode 100644
index 0000000000..acf616c4d4
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_004.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 219
+expression: "render_simple(r#\"\n@article{baez/article,\n author = {Baez, John C. and Lauda, Aaron D.},\n title = {Higher-Dimensional Algebra {V}: 2-Groups},\n journaltitle = {Theory and Applications of Categories},\n date = 2004,\n volume = 12,\n pages = {423-491},\n version = 3,\n eprint = {math/0307200v3},\n eprinttype = {arxiv},\n langid = {english},\n langidopts = {variant=american},\n annotation = {An \\texttt{article} with \\texttt{eprint} and\n \\texttt{eprinttype} fields. Note that the arXiv reference is\n transformed into a clickable link if \\texttt{hyperref} support\n has been enabled. Compare \\texttt{baez\\slash online}, which\n is the same item given as an \\texttt{online} entry},\n}\"#)"
+---
+Baez, John C., & Lauda, Aaron D. (n.d.). Higher-Dimensional Algebra V: 2-Groups. *Theory and Applications of Categories*, *12*, 423–491. Retrieved from [http://arxiv.org/abs/math/0307200v3](http://arxiv.org/abs/math/0307200v3)
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_005.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_005.snap
new file mode 100644
index 0000000000..0b6fc6a658
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_005.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 244
+expression: "render_simple(r#\"\n@string{jams = {J.~Amer. Math. Soc.}}\n\n@article{bertram,\n author = {Bertram, Aaron and Wentworth, Richard},\n title = {Gromov invariants for holomorphic maps on {Riemann} surfaces},\n journaltitle = jams,\n date = 1996,\n volume = 9,\n number = 2,\n pages = {529-571},\n langid = {english},\n langidopts = {variant=american},\n shorttitle = {Gromov invariants},\n annotation = {An \\texttt{article} entry with a \\texttt{volume} and a\n \\texttt{number} field},\n}\"#)"
+---
+Bertram, Aaron, & Wentworth, Richard. (n.d.). Gromov invariants for holomorphic maps on Riemann surfaces. *J. Amer. Math. Soc.*, *9*(2), 529–571.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_006.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_006.snap
new file mode 100644
index 0000000000..cf38243313
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_006.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 267
+expression: "render_simple(r#\"\n@article{doody,\n author = {Doody, Terrence},\n title = {Hemingway's Style and {Jake's} Narration},\n year = 1974,\n volume = 4,\n number = 3,\n pages = {212-225},\n langid = {english},\n langidopts = {variant=american},\n related = {matuz:doody},\n relatedstring= {\\autocap{e}xcerpt in},\n journal = {The Journal of Narrative Technique},\n annotation = {An \\texttt{article} entry cited as an excerpt from a\n \\texttt{collection} entry. Note the format of the\n \\texttt{related} and \\texttt{relatedstring} fields},\n}\"#)"
+---
+Doody, Terrence. (1974). Hemingway’s Style and Jake’s Narration. *The Journal of Narrative Technique*, *4*(3), 212–225.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_007.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_007.snap
new file mode 100644
index 0000000000..7ad251527a
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_007.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 290
+expression: "render_simple(r#\"\n@article{gillies,\n author = {Gillies, Alexander},\n title = {Herder and the Preparation of {Goethe's} Idea of World\n Literature},\n journaltitle = {Publications of the English Goethe Society},\n date = 1933,\n series = {newseries},\n volume = 9,\n pages = {46-67},\n langid = {english},\n langidopts = {variant=british},\n annotation = {An \\texttt{article} entry with a \\texttt{series} and a\n \\texttt{volume} field. Note that format of the \\texttt{series}\n field in the database file},\n}\"#)"
+---
+Gillies, Alexander. (n.d.). Herder and the Preparation of Goethe’s Idea of World Literature. *Publications of the English Goethe Society*, *9*, 46–67.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_008.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_008.snap
new file mode 100644
index 0000000000..1d99cc8d85
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_008.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 312
+expression: "render_simple(r#\"\n@article{glashow,\n author = {Glashow, Sheldon},\n title = {Partial Symmetries of Weak Interactions},\n journaltitle = {Nucl.~Phys.},\n date = 1961,\n volume = 22,\n pages = {579-588},\n}\"#)"
+---
+Glashow, Sheldon. (n.d.). Partial Symmetries of Weak Interactions. *Nucl. Phys.*, *22*, 579–588.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_009.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_009.snap
new file mode 100644
index 0000000000..8bd943fafb
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_009.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 327
+expression: "render_simple(r#\"\n@string{anch-ie = {Angew.~Chem. Int.~Ed.}}\n\n@article{herrmann,\n author = {Herrmann, Wolfgang A. and {\\\"O}fele, Karl and Schneider,\n Sabine K. and Herdtweck, Eberhardt and Hoffmann, Stephan D.},\n title = {A carbocyclic carbene as an efficient catalyst ligand for {C--C}\n coupling reactions},\n journaltitle = anch-ie,\n date = 2006,\n volume = 45,\n number = 23,\n pages = {3859-3862},\n indextitle = {Carbocyclic carbene as an efficient catalyst, A},\n}\"#)"
+---
+Herrmann, Wolfgang A., Öfele, Karl, Schneider, Sabine K., Herdtweck, Eberhardt, & Hoffmann, Stephan D. (n.d.). A carbocyclic carbene as an efficient catalyst ligand for C–C coupling reactions. *Angew. Chem. Int. Ed.*, *45*(23), 3859–3862.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_010.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_010.snap
new file mode 100644
index 0000000000..09e341c790
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_010.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 348
+expression: "render_simple(r#\"\n@string{jchph = {J.~Chem. Phys.}}\n\n@article{kastenholz,\n author = {Kastenholz, M. A. and H{\\\"u}nenberger, Philippe H.},\n title = {Computation of methodology\\hyphen independent ionic solvation\n free energies from molecular simulations},\n journaltitle = jchph,\n date = 2006,\n subtitle = {{I}. {The} electrostatic potential in molecular liquids},\n volume = 124,\n eid = 124106,\n doi = {10.1063/1.2172593},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Computation of ionic solvation free energies},\n annotation = {An \\texttt{article} entry with an \\texttt{eid} and a\n \\texttt{doi} field. Note that the \\textsc{doi} is transformed\n into a clickable link if \\texttt{hyperref} support has been\n enabled},\n abstract = {The computation of ionic solvation free energies from\n atomistic simulations is a surprisingly difficult problem that\n has found no satisfactory solution for more than 15 years. The\n reason is that the charging free energies evaluated from such\n simulations are affected by very large errors. One of these is\n related to the choice of a specific convention for summing up\n the contributions of solvent charges to the electrostatic\n potential in the ionic cavity, namely, on the basis of point\n charges within entire solvent molecules (M scheme) or on the\n basis of individual point charges (P scheme). The use of an\n inappropriate convention may lead to a charge-independent\n offset in the calculated potential, which depends on the\n details of the summation scheme, on the quadrupole-moment\n trace of the solvent molecule, and on the approximate form\n used to represent electrostatic interactions in the\n system. However, whether the M or P scheme (if any) represents\n the appropriate convention is still a matter of on-going\n debate. The goal of the present article is to settle this\n long-standing controversy by carefully analyzing (both\n analytically and numerically) the properties of the\n electrostatic potential in molecular liquids (and inside\n cavities within them).}, \n}\"#)"
+---
+Kastenholz, M. A., & Hünenberger, Philippe H. (n.d.). Computation of methodology\\hyphen independent ionic solvation free energies from molecular simulations: I. The electrostatic potential in molecular liquids. *J. Chem. Phys.*, *124*. [https://doi.org/10.1063/1.2172593](https://doi.org/10.1063/1.2172593)
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_011.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_011.snap
new file mode 100644
index 0000000000..cb07b2f730
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_011.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 397
+expression: "render_simple(r#\"\n@article{murray,\n author = {Hostetler, Michael J. and Wingate, Julia E. and Zhong,\n Chuan-Jian and Harris, Jay E. and Vachet, Richard W. and\n Clark, Michael R. and Londono, J. David and Green, Stephen\n J. and Stokes, Jennifer J. and Wignall, George D. and Glish,\n Gary L. and Porter, Marc D. and Evans, Neal D. and Murray,\n Royce W.},\n title = {Alkanethiolate gold cluster molecules with core diameters from\n 1.5 to 5.2~{nm}},\n journaltitle = {Langmuir},\n date = 1998,\n subtitle = {Core and monolayer properties as a function of core size},\n volume = 14,\n number = 1,\n pages = {17-30},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Alkanethiolate gold cluster molecules},\n shorttitle = {Alkanethiolate gold cluster molecules},\n annotation = {An \\texttt{article} entry with \\arabic{author} authors. By\n default, long author and editor lists are automatically\n truncated. This is configurable},\n}\"#)"
+---
+Hostetler, Michael J., Wingate, Julia E., Zhong, Chuan-Jian, Harris, Jay E., Vachet, Richard W., Clark, Michael R., … Murray, Royce W. (n.d.). Alkanethiolate gold cluster molecules with core diameters from 1.5 to 5.2 nm: Core and monolayer properties as a function of core size. *Langmuir*, *14*(1), 17–30.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_012.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_012.snap
new file mode 100644
index 0000000000..a99fa01b9c
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_012.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 427
+expression: "render_simple(r#\"\n@article{reese,\n author = {Reese, Trevor R.},\n title = {Georgia in {Anglo-Spanish} Diplomacy, 1736--1739},\n journaltitle = {William and Mary Quarterly},\n date = 1958,\n series = 3,\n volume = 15,\n pages = {168-190},\n langid = {english},\n langidopts = {variant=american},\n annotation = {An \\texttt{article} entry with a \\texttt{series} and a\n \\texttt{volume} field. Note the format of the series. If the\n value of the \\texttt{series} field is an integer, this number\n is printed as an ordinal and the string \\enquote*{series} is\n appended automatically},\n}\"#)"
+---
+Reese, Trevor R. (n.d.). Georgia in Anglo-Spanish Diplomacy, 1736–1739. *William and Mary Quarterly*, *15*, 168–190.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_013.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_013.snap
new file mode 100644
index 0000000000..cdca99c3b4
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_013.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 450
+expression: "render_simple(r#\"\n@article{sarfraz,\n author = {M. Sarfraz and M. F. A. Razzak},\n title = {Technical section: {An} algorithm for automatic capturing of\n the font outlines},\n year = 2002,\n volume = 26,\n number = 5,\n pages = {795-804},\n issn = {0097-8493},\n journal = {Computers and Graphics},\n annotation = {An \\texttt{article} entry with an \\texttt{issn} field},\n}\"#)"
+---
+Sarfraz, M., & Razzak, M. F. A. (2002). Technical section: An algorithm for automatic capturing of the font outlines. *Computers and Graphics*, *26*(5), 795–804.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_014.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_014.snap
new file mode 100644
index 0000000000..436d4fcbd9
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_014.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 469
+expression: "render_simple(r#\"\n@article{shore,\n author = {Shore, Bradd},\n title = {Twice-Born, Once Conceived},\n journaltitle = {American Anthropologist},\n date = {1991-03},\n subtitle = {Meaning Construction and Cultural Cognition},\n series = {newseries},\n volume = 93,\n number = 1,\n pages = {9-27},\n annotation = {An \\texttt{article} entry with \\texttt{series},\n \\texttt{volume}, and \\texttt{number} fields. Note the format\n of the \\texttt{series} which is a localization key},\n}\"#)"
+---
+Shore, Bradd. (n.d.). Twice-Born, Once Conceived: Meaning Construction and Cultural Cognition. *American Anthropologist*, *93*(1), 9–27.
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_015.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_015.snap
new file mode 100644
index 0000000000..4d787a627a
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_015.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 490
+expression: "render_simple(r#\"\n@article{sigfridsson,\n author = {Sigfridsson, Emma and Ryde, Ulf},\n title = {Comparison of methods for deriving atomic charges from the\n electrostatic potential and moments},\n journaltitle = {Journal of Computational Chemistry},\n date = 1998,\n volume = 19,\n number = 4,\n pages = {377-395},\n doi = {10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Methods for deriving atomic charges},\n annotation = {An \\texttt{article} entry with \\texttt{volume},\n \\texttt{number}, and \\texttt{doi} fields. Note that the\n \\textsc{doi} is transformed into a clickable link if\n \\texttt{hyperref} support has been enabled},\n abstract = {Four methods for deriving partial atomic charges from the\n quantum chemical electrostatic potential (CHELP, CHELPG,\n Merz-Kollman, and RESP) have been compared and critically\n evaluated. It is shown that charges strongly depend on how and\n where the potential points are selected. Two alternative\n methods are suggested to avoid the arbitrariness in the\n point-selection schemes and van der Waals exclusion radii:\n CHELP-BOW, which also estimates the charges from the\n electrostatic potential, but with potential points that are\n Boltzmann-weighted after their occurrence in actual\n simulations using the energy function of the program in which\n the charges will be used, and CHELMO, which estimates the\n charges directly from the electrostatic multipole\n moments. Different criteria for the quality of the charges are\n discussed.},\n}\"#)"
+---
+Sigfridsson, Emma, & Ryde, Ulf. (n.d.). Comparison of methods for deriving atomic charges from the electrostatic potential and moments. *Journal of Computational Chemistry*, *19*(4), 377–395. [https://doi.org/10.1002/(SICI)1096-987X(199803)19:4%3C377::AID-JCC1%3E3.0.CO;2-P](https://doi.org/10.1002/(SICI)1096-987X(199803)19:4%3C377::AID-JCC1%3E3.0.CO;2-P)
diff --git a/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_016.snap b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_016.snap
new file mode 100644
index 0000000000..1d08417a11
--- /dev/null
+++ b/support/texlab/src/snapshots/texlab__citation__tests__biblatex_examples_016.snap
@@ -0,0 +1,6 @@
+---
+source: src/citation.rs
+assertion_line: 530
+expression: "render_simple(r#\"\n@article{spiegelberg,\n author = {Spiegelberg, Herbert},\n title = {\\mkbibquote{Intention} und \\mkbibquote{Intentionalit{\\\"a}t} in\n der Scholastik, bei Brentano und Husserl},\n journaltitle = {Studia Philosophica},\n date = 1969,\n volume = 29,\n pages = {189-216},\n langid = {german},\n sorttitle = {Intention und Intentionalitat in der Scholastik, bei Brentano\n und Husserl},\n indexsorttitle= {Intention und Intentionalitat in der Scholastik, bei Brentano\n und Husserl},\n shorttitle = {Intention und Intentionalit{\\\"a}t},\n annotation = {An \\texttt{article} entry. Note the \\texttt{sorttitle} and\n \\texttt{indexsorttitle} fields and the markup of the quotes in\n the database file},\n}\"#)"
+---
+Spiegelberg, Herbert. (n.d.). Intention und Intentionalität in der Scholastik, bei Brentano und Husserl. *Studia Philosophica*, *29*, 189–216.
diff --git a/support/texlab/src/syntax.rs b/support/texlab/src/syntax.rs
index 5a906c0ea7..a0ae96089c 100644
--- a/support/texlab/src/syntax.rs
+++ b/support/texlab/src/syntax.rs
@@ -1,17 +1,3 @@
-use cstree::TextRange;
-
pub mod bibtex;
pub mod build_log;
pub mod latex;
-
-pub trait CstNode<'a> {
- type Lang: cstree::Language;
-
- fn cast(node: &'a cstree::ResolvedNode<Self::Lang>) -> Option<Self>
- where
- Self: Sized;
-
- fn syntax(&self) -> &'a cstree::ResolvedNode<Self::Lang>;
-
- fn small_range(&self) -> TextRange;
-}
diff --git a/support/texlab/src/syntax/bibtex.rs b/support/texlab/src/syntax/bibtex.rs
index c6e8bb3760..a35399419a 100644
--- a/support/texlab/src/syntax/bibtex.rs
+++ b/support/texlab/src/syntax/bibtex.rs
@@ -1,34 +1,71 @@
-mod cst;
-mod kind;
+mod ast;
mod lexer;
mod parser;
+#[cfg(test)]
+mod tests;
-pub use self::{
- cst::*,
- kind::SyntaxKind::{self, *},
- parser::{parse, Parse},
-};
+pub use self::{ast::*, parser::parse, SyntaxKind::*};
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Language {}
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+#[allow(non_camel_case_types)]
+#[repr(u16)]
+pub enum SyntaxKind {
+ WHITESPACE,
+ JUNK,
+ L_DELIM,
+ R_DELIM,
+ L_CURLY,
+ R_CURLY,
+ COMMA,
+ POUND,
+ QUOTE,
+ EQ,
+ TYPE,
+ WORD,
+ NAME,
+ INTEGER,
+ NBSP,
+ ACCENT_NAME,
+ COMMAND_NAME,
-impl cstree::Language for Language {
+ PREAMBLE,
+ STRING,
+ ENTRY,
+ FIELD,
+ VALUE,
+ LITERAL,
+ JOIN,
+ ACCENT,
+ COMMAND,
+ CURLY_GROUP,
+ QUOTE_GROUP,
+ ROOT,
+}
+
+impl From<SyntaxKind> for rowan::SyntaxKind {
+ fn from(kind: SyntaxKind) -> Self {
+ Self(kind as u16)
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+pub enum Lang {}
+
+impl rowan::Language for Lang {
type Kind = SyntaxKind;
- fn kind_from_raw(raw: cstree::SyntaxKind) -> Self::Kind {
- assert!(raw.0 <= ROOT as u16);
+ fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
+ assert!(raw.0 <= SyntaxKind::ROOT as u16);
unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
}
- fn kind_to_raw(kind: Self::Kind) -> cstree::SyntaxKind {
+ fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
-pub type SyntaxNode = cstree::ResolvedNode<Language>;
-
-pub type SyntaxToken = cstree::ResolvedToken<Language>;
+pub type SyntaxNode = rowan::SyntaxNode<Lang>;
-pub type SyntaxElement = cstree::ResolvedElement<Language>;
+pub type SyntaxToken = rowan::SyntaxToken<Lang>;
-pub type SyntaxElementRef<'a> = cstree::ResolvedElementRef<'a, Language>;
+pub type SyntaxElement = rowan::SyntaxElement<Lang>;
diff --git a/support/texlab/src/syntax/bibtex/ast.rs b/support/texlab/src/syntax/bibtex/ast.rs
new file mode 100644
index 0000000000..10ac571dd2
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/ast.rs
@@ -0,0 +1,244 @@
+use rowan::{ast::AstNode, NodeOrToken};
+
+use super::{
+ Lang,
+ SyntaxKind::{self, *},
+ SyntaxNode, SyntaxToken,
+};
+
+macro_rules! ast_node {
+ (name: $name:ident, kinds: [$($kind:pat),+], traits: [$($trait: ident),*]) => {
+ #[derive(Clone)]
+ pub struct $name {
+ node: SyntaxNode,
+ }
+
+ impl AstNode for $name {
+ type Language = Lang;
+
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ $($kind => true,)+
+ _ => false,
+ }
+ }
+
+ fn cast(node: SyntaxNode) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ match node.kind() {
+ $($kind => Some(Self { node}),)+
+ _ => None,
+ }
+ }
+
+ fn syntax(&self) -> &SyntaxNode {
+ &self.node
+ }
+ }
+
+ $(
+ impl $trait for $name { }
+ )*
+ };
+}
+
+macro_rules! ast_node_enum {
+ (name: $name:ident, variants: [$($variant:ident),+]) => {
+ #[derive(Clone)]
+ pub enum $name {
+ $($variant($variant),)*
+ }
+
+ impl AstNode for $name {
+ type Language = Lang;
+
+ fn can_cast(kind: SyntaxKind) -> bool {
+ false $(|| $variant::can_cast(kind))+
+ }
+
+ fn cast(node: SyntaxNode) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ None $(.or_else(|| $variant::cast(node.clone()).map(Self::$variant)))*
+ }
+
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ $(Self::$variant(node) => node.syntax(),)*
+ }
+ }
+ }
+
+ $(
+ impl From<$variant> for $name {
+ fn from(node: $variant) -> Self {
+ Self::$variant(node)
+ }
+ }
+ )*
+ };
+}
+
+pub trait HasType: AstNode<Language = Lang> {
+ fn type_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == TYPE)
+ }
+}
+
+pub trait HasDelims: AstNode<Language = Lang> {
+ fn left_delim_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == L_DELIM)
+ }
+
+ fn right_delim_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == R_DELIM)
+ }
+}
+
+pub trait HasName: AstNode<Language = Lang> {
+ fn name_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == NAME)
+ }
+}
+
+pub trait HasEq: AstNode<Language = Lang> {
+ fn eq_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == NAME)
+ }
+}
+
+pub trait HasComma: AstNode<Language = Lang> {
+ fn comma_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == COMMA)
+ }
+}
+
+pub trait HasPound: AstNode<Language = Lang> {
+ fn pound_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == POUND)
+ }
+}
+
+pub trait HasInteger: AstNode<Language = Lang> {
+ fn integer_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == INTEGER)
+ }
+}
+
+pub trait HasCommandName: AstNode<Language = Lang> {
+ fn command_name_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == COMMAND_NAME)
+ }
+}
+
+pub trait HasAccentName: AstNode<Language = Lang> {
+ fn accent_name_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == ACCENT_NAME)
+ }
+}
+
+pub trait HasWord: AstNode<Language = Lang> {
+ fn word_token(&self) -> Option<SyntaxToken> {
+ self.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|token| token.kind() == WORD)
+ }
+}
+
+pub trait HasValue: AstNode<Language = Lang> {
+ fn value(&self) -> Option<Value> {
+ self.syntax().children().find_map(Value::cast)
+ }
+}
+
+ast_node!(name: Root, kinds: [ROOT], traits: []);
+
+impl Root {
+ pub fn strings(&self) -> impl Iterator<Item = StringDef> {
+ self.syntax().children().filter_map(StringDef::cast)
+ }
+
+ pub fn entries(&self) -> impl Iterator<Item = Entry> {
+ self.syntax().children().filter_map(Entry::cast)
+ }
+
+ pub fn find_entry(&self, name: &str) -> Option<Entry> {
+ self.entries().find(|entry| {
+ entry
+ .name_token()
+ .map_or(false, |token| token.text() == name)
+ })
+ }
+}
+
+ast_node!(name: Preamble, kinds: [PREAMBLE], traits: [HasType, HasDelims, HasValue]);
+
+ast_node!(name: StringDef, kinds: [STRING], traits: [HasType, HasDelims, HasName, HasEq, HasValue]);
+
+ast_node!(name: Entry, kinds: [ENTRY], traits: [HasType, HasDelims, HasName, HasComma]);
+
+impl Entry {
+ pub fn fields(&self) -> impl Iterator<Item = Field> {
+ self.syntax().children().filter_map(Field::cast)
+ }
+}
+
+ast_node!(name: Field, kinds: [FIELD], traits: [HasName, HasEq, HasValue, HasComma]);
+
+ast_node_enum!(name: Value, variants: [Literal, CurlyGroup, QuoteGroup, Join, Accent, Command]);
+
+ast_node!(name: Literal, kinds: [LITERAL], traits: [HasName, HasInteger]);
+
+ast_node!(name: CurlyGroup, kinds: [CURLY_GROUP], traits: []);
+
+ast_node!(name: QuoteGroup, kinds: [QUOTE_GROUP], traits: []);
+
+ast_node!(name: Join, kinds: [JOIN], traits: [HasPound]);
+
+impl Join {
+ pub fn left_value(&self) -> Option<Value> {
+ self.syntax().children().find_map(Value::cast)
+ }
+
+ pub fn right_value(&self) -> Option<Value> {
+ self.syntax().children().filter_map(Value::cast).nth(1)
+ }
+}
+
+ast_node!(name: Accent, kinds: [ACCENT], traits: [HasAccentName, HasWord]);
+
+ast_node!(name: Command, kinds: [COMMAND], traits: [HasCommandName]);
diff --git a/support/texlab/src/syntax/bibtex/cst.rs b/support/texlab/src/syntax/bibtex/cst.rs
deleted file mode 100644
index ba860c1ee7..0000000000
--- a/support/texlab/src/syntax/bibtex/cst.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use cstree::TextRange;
-use itertools::{EitherOrBoth, Itertools};
-
-use crate::syntax::CstNode;
-
-use super::{Language, SyntaxKind::*, SyntaxNode, SyntaxToken};
-
-macro_rules! cst_node {
- ($name:ident, $($kind:pat),+) => {
- #[derive(Clone)]
- #[repr(transparent)]
- pub struct $name<'a>(&'a SyntaxNode);
-
- impl<'a> CstNode<'a> for $name<'a> {
- type Lang = Language;
-
- fn cast(node: &'a cstree::ResolvedNode<Self::Lang>) -> Option<Self>
- where
- Self: Sized,
- {
- match node.kind() {
- $($kind => Some(Self(node)),)+
- _ => None,
- }
- }
-
- fn syntax(&self) -> &'a cstree::ResolvedNode<Self::Lang> {
- &self.0
- }
-
- fn small_range(&self) -> TextRange {
- let full_range = self.syntax().text_range();
- let start = full_range.start();
- let mut token = self.syntax().last_token();
- while let Some(current) = token {
- if !matches!(current.kind(), WHITESPACE | JUNK) {
- return TextRange::new(start, current.text_range().end());
- }
- token = current.prev_token();
- }
- TextRange::new(start, start)
- }
- }
- };
-}
-
-pub trait HasCurly<'a>: CstNode<'a, Lang = Language> {
- fn left_curly(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == L_CURLY.into())
- }
-
- fn right_curly(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == R_CURLY.into())
- }
-}
-
-pub trait HasQuotes<'a>: CstNode<'a, Lang = Language> {
- fn left_quote(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == QUOTE.into())
- }
-
- fn right_quote(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .filter(|node| node.kind() == QUOTE.into())
- .nth(1)
- }
-}
-
-pub trait HasDelimiters<'a>: CstNode<'a, Lang = Language> {
- fn left_delimiter(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| matches!(node.kind(), L_CURLY | L_PAREN))
- }
-
- fn right_delimiter(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| matches!(node.kind(), R_CURLY | R_PAREN))
- }
-}
-
-pub trait HasType<'a>: CstNode<'a, Lang = Language> {
- fn ty(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| {
- matches!(
- node.kind(),
- PREAMBLE_TYPE | STRING_TYPE | COMMENT_TYPE | ENTRY_TYPE
- )
- })
- }
-}
-
-cst_node!(Root, ROOT);
-
-cst_node!(Junk, JUNK);
-
-cst_node!(Comment, COMMENT);
-
-impl<'a> HasType<'a> for Comment<'a> {}
-
-cst_node!(Preamble, PREAMBLE);
-
-impl<'a> HasType<'a> for Preamble<'a> {}
-
-impl<'a> HasDelimiters<'a> for Preamble<'a> {}
-
-impl<'a> Preamble<'a> {
- pub fn value(&self) -> Option<Value<'a>> {
- self.syntax().children().find_map(Value::cast)
- }
-}
-
-cst_node!(String, STRING);
-
-impl<'a> HasType<'a> for String<'a> {}
-
-impl<'a> HasDelimiters<'a> for String<'a> {}
-
-impl<'a> String<'a> {
- pub fn name(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == WORD)
- }
-
- pub fn equality_sign(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == EQUALITY_SIGN)
- }
-
- pub fn value(&self) -> Option<Value<'a>> {
- self.syntax().children().find_map(Value::cast)
- }
-}
-
-cst_node!(Entry, ENTRY);
-
-impl<'a> HasType<'a> for Entry<'a> {}
-
-impl<'a> HasDelimiters<'a> for Entry<'a> {}
-
-impl<'a> Entry<'a> {
- pub fn key(&self) -> Option<Key<'a>> {
- self.syntax().children().find_map(Key::cast)
- }
-
- pub fn fields(&self) -> impl Iterator<Item = Field<'a>> {
- self.syntax().children().filter_map(Field::cast)
- }
-}
-
-cst_node!(Key, KEY);
-
-impl<'a> Key<'a> {
- pub fn words(&self) -> impl Iterator<Item = &'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .filter(|node| node.kind() == WORD)
- }
-}
-
-impl<'a> PartialEq for Key<'a> {
- fn eq(&self, other: &Self) -> bool {
- self.words()
- .zip_longest(other.words())
- .all(|result| match result {
- EitherOrBoth::Both(left, right) => left.text() == right.text(),
- EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => false,
- })
- }
-}
-
-impl<'a> Eq for Key<'a> {}
-
-impl<'a> ToString for Key<'a> {
- fn to_string(&self) -> std::string::String {
- self.words().map(|word| word.text()).join(" ")
- }
-}
-
-cst_node!(Field, FIELD);
-
-impl<'a> Field<'a> {
- pub fn name(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == WORD)
- }
-
- pub fn equality_sign(&self) -> Option<&'a SyntaxToken> {
- self.syntax()
- .children_with_tokens()
- .filter_map(|node| node.into_token())
- .find(|node| node.kind() == EQUALITY_SIGN)
- }
-
- pub fn value(&self) -> Option<Value<'a>> {
- self.syntax().children().find_map(Value::cast)
- }
-}
-
-cst_node!(Value, VALUE);
-
-impl<'a> Value<'a> {
- pub fn tokens(&self) -> impl Iterator<Item = Token<'a>> {
- self.syntax().children().filter_map(Token::cast)
- }
-}
-
-cst_node!(Token, TOKEN);
-
-cst_node!(BraceGroup, BRACE_GROUP);
-
-impl<'a> HasCurly<'a> for BraceGroup<'a> {}
-
-cst_node!(QuoteGroup, QUOTE_GROUP);
-
-impl<'a> HasQuotes<'a> for QuoteGroup<'a> {}
diff --git a/support/texlab/src/syntax/bibtex/kind.rs b/support/texlab/src/syntax/bibtex/kind.rs
deleted file mode 100644
index 91f62fd3b8..0000000000
--- a/support/texlab/src/syntax/bibtex/kind.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
-#[allow(non_camel_case_types)]
-#[repr(u16)]
-pub enum SyntaxKind {
- ERROR = 0,
- MISSING,
-
- WHITESPACE,
- PREAMBLE_TYPE,
- STRING_TYPE,
- COMMENT_TYPE,
- ENTRY_TYPE,
- WORD,
- L_CURLY,
- R_CURLY,
- L_PAREN,
- R_PAREN,
- COMMA,
- HASH,
- QUOTE,
- EQUALITY_SIGN,
- COMMAND_NAME,
-
- JUNK,
- PREAMBLE,
- STRING,
- COMMENT,
- ENTRY,
- KEY,
- FIELD,
- VALUE,
- TOKEN,
- BRACE_GROUP,
- QUOTE_GROUP,
- ROOT,
-}
-
-impl SyntaxKind {
- pub fn is_type(&self) -> bool {
- use SyntaxKind::*;
- matches!(
- self,
- PREAMBLE_TYPE | STRING_TYPE | COMMENT_TYPE | ENTRY_TYPE
- )
- }
-}
-
-impl From<SyntaxKind> for cstree::SyntaxKind {
- fn from(kind: SyntaxKind) -> Self {
- Self(kind as u16)
- }
-}
diff --git a/support/texlab/src/syntax/bibtex/lexer.rs b/support/texlab/src/syntax/bibtex/lexer.rs
index dff111d645..3ea8e9f8d2 100644
--- a/support/texlab/src/syntax/bibtex/lexer.rs
+++ b/support/texlab/src/syntax/bibtex/lexer.rs
@@ -1,82 +1,180 @@
use logos::Logos;
-use super::kind::SyntaxKind;
+use super::SyntaxKind::{self, *};
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Logos)]
-#[allow(non_camel_case_types)]
-#[repr(u16)]
-enum LogosToken {
- #[regex(r"\s+")]
- WHITESPACE = 2,
-
- #[regex(r"@[Pp][Rr][Ee][Aa][Mm][Bb][Ll][Ee]")]
- PREAMBLE_TYPE,
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)]
+pub enum RootToken {
+ #[token(r"@preamble", ignore(ascii_case))]
+ Preamble,
- #[regex(r"@[Ss][Tt][Rr][Ii][Nn][Gg]")]
- STRING_TYPE,
+ #[token(r"@string", ignore(ascii_case))]
+ String,
- #[regex(r"@[Cc][Oo][Mm][Mm][Ee][Nn][Tt]")]
- COMMENT_TYPE,
+ #[token(r"@comment", ignore(ascii_case))]
+ Comment,
- #[regex(r"@[!\$\&\*\+\-\./:;<>\?@\[\]\\\^_`\|\~a-zA-Z][!\$\&\*\+\-\./:;<>\?@\[\]\\\^_`\|\~a-zA-Z0-9]*|@")]
- ENTRY_TYPE,
+ #[regex(r"@[a-zA-Z]*")]
+ Entry,
- #[regex(r#"[^\s\{\}\(\),#"=\\]+"#)]
+ #[regex(r"[^@]+")]
#[error]
- WORD,
+ Junk,
+}
- #[token("{")]
- L_CURLY,
+impl From<RootToken> for SyntaxKind {
+ fn from(token: RootToken) -> Self {
+ match token {
+ RootToken::Preamble | RootToken::String | RootToken::Comment | RootToken::Entry => TYPE,
+ RootToken::Junk => JUNK,
+ }
+ }
+}
- #[token("}")]
- R_CURLY,
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)]
+pub enum BodyToken {
+ #[regex(r"\s+")]
+ Whitespace,
+ #[token("{")]
#[token("(")]
- L_PAREN,
+ LDelim,
+ #[token("}")]
#[token(")")]
- R_PAREN,
+ RDelim,
#[token(",")]
- COMMA,
+ Comma,
- #[token("#")]
- HASH,
+ #[token("=")]
+ Eq,
- #[token("\"")]
- QUOTE,
+ #[regex(r"[^\s\(\)\{\}@,=]+")]
+ Name,
- #[token("=")]
- EQUALITY_SIGN,
+ #[error]
+ Error,
+}
- #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
- COMMAND_NAME,
+impl From<BodyToken> for SyntaxKind {
+ fn from(token: BodyToken) -> Self {
+ match token {
+ BodyToken::Whitespace => WHITESPACE,
+ BodyToken::LDelim => L_DELIM,
+ BodyToken::RDelim => R_DELIM,
+ BodyToken::Comma => COMMA,
+ BodyToken::Eq => EQ,
+ BodyToken::Name => NAME,
+ BodyToken::Error => unreachable!(),
+ }
+ }
}
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Lexer<'a> {
- tokens: Vec<(SyntaxKind, &'a str)>,
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)]
+pub enum ValueToken {
+ #[regex(r"\s+")]
+ Whitespace,
+
+ #[token("#")]
+ Pound,
+
+ #[token(",")]
+ Comma,
+
+ #[token("{")]
+ LCurly,
+
+ #[token("}")]
+ RCurly,
+
+ #[token("\"")]
+ Quote,
+
+ #[regex(r"\d+", priority = 2)]
+ Integer,
+
+ #[regex(r#"[^\s"\{\},]+"#)]
+ #[error]
+ Name,
}
-impl<'a> Lexer<'a> {
- pub fn new(text: &'a str) -> Self {
- let mut tokens = Vec::new();
- let mut lexer = LogosToken::lexer(text);
- while let Some(kind) = lexer.next() {
- tokens.push((
- unsafe { std::mem::transmute::<LogosToken, SyntaxKind>(kind) },
- lexer.slice(),
- ));
+impl From<ValueToken> for SyntaxKind {
+ fn from(token: ValueToken) -> Self {
+ match token {
+ ValueToken::Whitespace => WHITESPACE,
+ ValueToken::Pound => POUND,
+ ValueToken::Comma => COMMA,
+ ValueToken::LCurly => L_CURLY,
+ ValueToken::RCurly => R_CURLY,
+ ValueToken::Quote => QUOTE,
+ ValueToken::Integer => INTEGER,
+ ValueToken::Name => NAME,
}
- tokens.reverse();
- Self { tokens }
}
+}
- pub fn peek(&self) -> Option<SyntaxKind> {
- self.tokens.last().map(|(kind, _)| *kind)
- }
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)]
+pub enum ContentToken {
+ #[regex(r"\s+")]
+ Whitespace,
+
+ #[token(",")]
+ Comma,
- pub fn consume(&mut self) -> Option<(SyntaxKind, &'a str)> {
- self.tokens.pop()
+ #[token("{")]
+ LCurly,
+
+ #[token("}")]
+ RCurly,
+
+ #[token("\"")]
+ Quote,
+
+ #[regex(r"\d+", priority = 2)]
+ Integer,
+
+ #[token(r#"~"#)]
+ Nbsp,
+
+ #[token(r#"\`"#)]
+ #[token(r#"\'"#)]
+ #[token(r#"\^"#)]
+ #[token(r#"\""#)]
+ #[token(r#"\H"#)]
+ #[token(r#"\~"#)]
+ #[token(r#"\c"#)]
+ #[token(r#"\k"#)]
+ #[token(r#"\="#)]
+ #[token(r#"\b"#)]
+ #[token(r#"\."#)]
+ #[token(r#"\d"#)]
+ #[token(r#"\r"#)]
+ #[token(r#"\u"#)]
+ #[token(r#"\v"#)]
+ #[token(r#"\t"#)]
+ AccentName,
+
+ #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
+ CommandName,
+
+ #[regex(r#"[^\s"\{\}\\~,]+"#)]
+ #[error]
+ Word,
+}
+
+impl From<ContentToken> for SyntaxKind {
+ fn from(token: ContentToken) -> Self {
+ match token {
+ ContentToken::Whitespace => WHITESPACE,
+ ContentToken::Comma => COMMA,
+ ContentToken::LCurly => L_CURLY,
+ ContentToken::RCurly => R_CURLY,
+ ContentToken::Quote => QUOTE,
+ ContentToken::Integer => INTEGER,
+ ContentToken::Nbsp => NBSP,
+ ContentToken::AccentName => ACCENT_NAME,
+ ContentToken::CommandName => COMMAND_NAME,
+ ContentToken::Word => WORD,
+ }
}
}
diff --git a/support/texlab/src/syntax/bibtex/parser.rs b/support/texlab/src/syntax/bibtex/parser.rs
index 6e8c4b3b59..4a8a429b18 100644
--- a/support/texlab/src/syntax/bibtex/parser.rs
+++ b/support/texlab/src/syntax/bibtex/parser.rs
@@ -1,425 +1,264 @@
-use cstree::GreenNodeBuilder;
+use logos::{Lexer, Logos};
+use rowan::{GreenNode, GreenNodeBuilder};
use super::{
- lexer::Lexer,
+ lexer::*,
SyntaxKind::{self, *},
- SyntaxNode,
};
-#[derive(Debug, Clone)]
-pub struct Parse {
- pub root: SyntaxNode,
-}
-
-struct Parser<'a> {
- lexer: Lexer<'a>,
- builder: GreenNodeBuilder<'static, 'static>,
-}
-
-impl<'a> Parser<'a> {
- pub fn new(lexer: Lexer<'a>) -> Self {
- Self {
- lexer,
- builder: GreenNodeBuilder::new(),
- }
- }
-
- fn peek(&self) -> Option<SyntaxKind> {
- self.lexer.peek()
- }
-
- fn eat(&mut self) {
- let (kind, text) = self.lexer.consume().unwrap();
- self.builder.token(kind.into(), text);
- }
-
- fn expect(&mut self, kind: SyntaxKind) {
- if self.peek() == Some(kind) {
- self.eat();
- self.trivia();
- } else {
- self.builder.token(MISSING.into(), "");
+pub fn parse(input: &str) -> GreenNode {
+ let mut ptr = TokenPtr {
+ builder: GreenNodeBuilder::new(),
+ lexer: RootToken::lexer(input),
+ token: None,
+ };
+
+ ptr.builder.start_node(ROOT.into());
+
+ while let Some(token) = ptr.current() {
+ match token {
+ RootToken::Preamble => ptr = preamble(ptr),
+ RootToken::String => ptr = string(ptr),
+ RootToken::Entry => ptr = entry(ptr),
+ RootToken::Comment | RootToken::Junk => ptr.bump(),
}
}
- pub fn parse(mut self) -> Parse {
- self.builder.start_node(ROOT.into());
- while let Some(kind) = self.peek() {
- match kind {
- PREAMBLE_TYPE => self.preamble(),
- STRING_TYPE => self.string(),
- COMMENT_TYPE => self.comment(),
- ENTRY_TYPE => self.entry(),
- _ => self.junk(),
- }
- }
- self.builder.finish_node();
- let (green, resolver) = self.builder.finish();
- Parse {
- root: SyntaxNode::new_root_with_resolver(green, resolver.unwrap()),
- }
- }
-
- fn trivia(&mut self) {
- while self.peek() == Some(WHITESPACE) {
- self.eat();
- }
- }
-
- fn junk(&mut self) {
- self.builder.start_node(JUNK.into());
- while self
- .lexer
- .peek()
- .filter(|&kind| {
- !matches!(
- kind,
- PREAMBLE_TYPE | STRING_TYPE | COMMENT_TYPE | ENTRY_TYPE,
- )
- })
- .is_some()
- {
- self.eat();
- }
- self.builder.finish_node();
- }
-
- fn left_delimiter_or_missing(&mut self) {
- if self
- .lexer
- .peek()
- .filter(|&kind| matches!(kind, L_CURLY | L_PAREN))
- .is_some()
- {
- self.eat();
- self.trivia();
- } else {
- self.builder.token(MISSING.into(), "");
- }
- }
-
- fn right_delimiter_or_missing(&mut self) {
- if self
- .lexer
- .peek()
- .filter(|&kind| matches!(kind, R_CURLY | R_PAREN))
- .is_some()
- {
- self.eat();
- self.trivia();
- } else {
- self.builder.token(MISSING.into(), "");
- }
- }
-
- fn value_or_missing(&mut self) {
- if self
- .lexer
- .peek()
- .filter(|&kind| matches!(kind, L_CURLY | QUOTE | WORD))
- .is_some()
- {
- self.value();
- } else {
- self.builder.token(MISSING.into(), "");
- }
- }
-
- fn preamble(&mut self) {
- self.builder.start_node(PREAMBLE.into());
- self.eat();
- self.trivia();
- self.left_delimiter_or_missing();
- self.value_or_missing();
- self.right_delimiter_or_missing();
- self.builder.finish_node();
- }
-
- fn string(&mut self) {
- self.builder.start_node(STRING.into());
- self.eat();
- self.trivia();
- self.left_delimiter_or_missing();
-
- if self.peek() != Some(WORD) {
- self.builder.token(MISSING.into(), "");
- self.builder.finish_node();
- return;
- }
- self.eat();
- self.trivia();
- self.expect(EQUALITY_SIGN);
-
- self.value_or_missing();
-
- self.right_delimiter_or_missing();
- self.builder.finish_node();
- }
-
- fn comment(&mut self) {
- self.builder.start_node(COMMENT.into());
- self.eat();
- self.builder.finish_node();
- }
-
- fn entry(&mut self) {
- self.builder.start_node(ENTRY.into());
- self.eat();
- self.trivia();
-
- self.left_delimiter_or_missing();
-
- if self.peek() != Some(WORD) {
- self.builder.token(MISSING.into(), "");
- self.builder.finish_node();
- return;
- }
- self.key();
-
- while let Some(kind) = self.peek() {
- match kind {
- WHITESPACE => self.eat(),
- WORD => self.field(),
- COMMA => self.eat(),
- _ => break,
- };
- }
-
- self.right_delimiter_or_missing();
-
- self.builder.finish_node();
- }
-
- fn key(&mut self) {
- self.builder.start_node(KEY.into());
- while self
- .peek()
- .filter(|&kind| matches!(kind, WORD | WHITESPACE))
- .is_some()
- {
- self.eat();
- }
- self.builder.finish_node();
- }
-
- fn field(&mut self) {
- self.builder.start_node(FIELD.into());
- self.eat();
- self.trivia();
+ ptr.builder.finish_node();
+ ptr.builder.finish()
+}
- if self.peek() == Some(EQUALITY_SIGN) {
- self.eat();
- self.trivia();
- } else {
- self.builder.token(MISSING.into(), "");
- }
+fn preamble(mut ptr: TokenPtr<RootToken>) -> TokenPtr<RootToken> {
+ ptr.builder.start_node(PREAMBLE.into());
+ ptr.bump();
+ let mut ptr = ptr.morph();
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::LDelim);
+ ptr.expect(BodyToken::Whitespace);
+ ptr = value(ptr.morph()).morph();
+ ptr.expect(BodyToken::RDelim);
+ ptr.builder.finish_node();
+ ptr.morph()
+}
- if self
- .lexer
- .peek()
- .filter(|&kind| matches!(kind, L_CURLY | QUOTE | WORD))
- .is_some()
- {
- self.value();
- } else {
- self.builder.token(MISSING.into(), "");
- }
+fn string(mut ptr: TokenPtr<RootToken>) -> TokenPtr<RootToken> {
+ ptr.builder.start_node(STRING.into());
+ ptr.bump();
+ let mut ptr = ptr.morph();
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::LDelim);
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Name);
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Eq);
+ ptr.expect(BodyToken::Whitespace);
+ ptr = value(ptr.morph()).morph();
+ ptr.expect(BodyToken::RDelim);
+ ptr.builder.finish_node();
+ ptr.morph()
+}
- self.builder.finish_node();
- }
+fn entry(mut ptr: TokenPtr<RootToken>) -> TokenPtr<RootToken> {
+ ptr.builder.start_node(ENTRY.into());
+ ptr.bump();
+ let mut ptr = ptr.morph();
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::LDelim);
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Name);
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Comma);
+ ptr.expect(BodyToken::Whitespace);
+
+ while ptr.at(BodyToken::Name) {
+ ptr = field(ptr);
+ ptr.expect(BodyToken::Whitespace);
+ }
+
+ ptr.expect(BodyToken::RDelim);
+ ptr.builder.finish_node();
+ ptr.morph()
+}
- fn value(&mut self) {
- self.builder.start_node(VALUE.into());
- self.token();
- while let Some(kind) = self.peek() {
- match kind {
- WHITESPACE => self.eat(),
- L_CURLY | QUOTE | WORD => self.token(),
- HASH => self.eat(),
- _ => break,
- }
- }
- self.builder.finish_node();
- }
+fn field(mut ptr: TokenPtr<BodyToken>) -> TokenPtr<BodyToken> {
+ ptr.builder.start_node(FIELD.into());
+ ptr.bump();
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Eq);
+ ptr.expect(BodyToken::Whitespace);
+ ptr = value(ptr.morph()).morph();
+ ptr.expect(BodyToken::Whitespace);
+ ptr.expect(BodyToken::Comma);
+ ptr.builder.finish_node();
+ ptr
+}
- fn token(&mut self) {
- self.builder.start_node(TOKEN.into());
- match self.peek().unwrap() {
- L_CURLY => self.brace_group(),
- QUOTE => self.quote_group(),
- WORD => self.eat(),
- _ => unreachable!(),
+fn value(mut ptr: TokenPtr<ValueToken>) -> TokenPtr<ValueToken> {
+ let checkpoint = ptr.builder.checkpoint();
+ if let Some(token) = ptr.current() {
+ match token {
+ ValueToken::Whitespace => unreachable!(),
+ ValueToken::Pound | ValueToken::Comma | ValueToken::RCurly => return ptr,
+ ValueToken::Integer | ValueToken::Name => ptr = literal(ptr),
+ ValueToken::LCurly => ptr = curly_group(ptr.morph()).morph(),
+ ValueToken::Quote => ptr = quote_group(ptr.morph()).morph(),
};
- self.builder.finish_node();
- }
- fn brace_group(&mut self) {
- self.builder.start_node(BRACE_GROUP.into());
- self.eat();
-
- while let Some(kind) = self.peek() {
- match kind {
- WHITESPACE => self.eat(),
- PREAMBLE_TYPE => break,
- STRING_TYPE => break,
- COMMENT_TYPE => break,
- ENTRY_TYPE => break,
- WORD => self.eat(),
- L_CURLY => self.brace_group(),
- R_CURLY => break,
- L_PAREN => self.eat(),
- R_PAREN => self.eat(),
- COMMA => self.eat(),
- HASH => self.eat(),
- QUOTE => self.eat(),
- EQUALITY_SIGN => self.eat(),
- COMMAND_NAME => self.eat(),
- _ => unreachable!(),
- };
+ ptr.expect(ValueToken::Whitespace);
+ if ptr.at(ValueToken::Pound) {
+ ptr.builder.start_node_at(checkpoint, JOIN.into());
+ ptr.bump();
+ ptr.expect(ValueToken::Whitespace);
+ ptr = value(ptr);
+ ptr.builder.finish_node();
}
-
- self.expect(R_CURLY);
-
- self.builder.finish_node();
}
- fn quote_group(&mut self) {
- self.builder.start_node(QUOTE_GROUP.into());
- self.eat();
-
- while let Some(kind) = self.peek() {
- match kind {
- WHITESPACE => self.eat(),
- PREAMBLE_TYPE => break,
- STRING_TYPE => break,
- COMMENT_TYPE => break,
- ENTRY_TYPE => break,
- WORD => self.eat(),
- L_CURLY => self.brace_group(),
- R_CURLY => break,
- L_PAREN => self.eat(),
- R_PAREN => self.eat(),
- COMMA => self.eat(),
- HASH => self.eat(),
- QUOTE => break,
- EQUALITY_SIGN => self.eat(),
- COMMAND_NAME => self.eat(),
- _ => unreachable!(),
- };
- }
-
- self.expect(QUOTE);
- self.builder.finish_node();
- }
+ ptr
}
-pub fn parse(text: &str) -> Parse {
- Parser::new(Lexer::new(text)).parse()
+fn literal(mut ptr: TokenPtr<ValueToken>) -> TokenPtr<ValueToken> {
+ ptr.builder.start_node(LITERAL.into());
+ ptr.bump();
+ ptr.builder.finish_node();
+ ptr
}
-#[cfg(test)]
-mod tests {
- use insta::assert_debug_snapshot;
-
- use super::*;
-
- fn setup(text: &str) -> SyntaxNode {
- parse(&text.trim().replace("\r", "")).root
- }
-
- #[test]
- fn test_empty() {
- assert_debug_snapshot!(setup(r#""#));
+fn curly_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> {
+ ptr.builder.start_node(CURLY_GROUP.into());
+ ptr.bump();
+ ptr.expect(ContentToken::Whitespace);
+
+ while let Some(token) = ptr.current() {
+ match token {
+ ContentToken::RCurly => break,
+ ContentToken::Whitespace
+ | ContentToken::Nbsp
+ | ContentToken::Comma
+ | ContentToken::Integer
+ | ContentToken::Word => ptr.bump(),
+ ContentToken::LCurly => ptr = curly_group(ptr),
+ ContentToken::Quote => ptr = quote_group(ptr),
+ ContentToken::AccentName => ptr = accent(ptr),
+ ContentToken::CommandName => ptr = command(ptr),
+ };
}
- #[test]
- fn test_junk() {
- assert_debug_snapshot!(setup(r#"Hello World!"#));
- }
+ ptr.expect(ContentToken::RCurly);
+ ptr.builder.finish_node();
+ ptr
+}
- #[test]
- fn test_preamble_complete() {
- assert_debug_snapshot!(setup(r#"@preamble{ "Hello World" }"#));
+fn quote_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> {
+ ptr.builder.start_node(QUOTE_GROUP.into());
+ ptr.bump();
+ ptr.expect(ContentToken::Whitespace);
+
+ while let Some(token) = ptr.current() {
+ match token {
+ ContentToken::Quote => break,
+ ContentToken::Whitespace
+ | ContentToken::Nbsp
+ | ContentToken::Comma
+ | ContentToken::RCurly
+ | ContentToken::Integer
+ | ContentToken::Word => ptr.bump(),
+ ContentToken::LCurly => ptr = curly_group(ptr),
+ ContentToken::AccentName => ptr = accent(ptr),
+ ContentToken::CommandName => ptr = command(ptr),
+ };
}
- #[test]
- fn test_preamble_missing_end() {
- assert_debug_snapshot!(setup(r#"@preamble{ "Hello World" "#));
- }
+ ptr.expect(ContentToken::Quote);
+ ptr.builder.finish_node();
+ ptr
+}
- #[test]
- fn test_preamble_casing() {
- assert_debug_snapshot!(setup(r#"@preAmbLe{ "Hello World" }"#));
- }
+fn accent(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> {
+ ptr.builder.start_node(ACCENT.into());
+ ptr.bump();
+ ptr.expect(ContentToken::Whitespace);
- #[test]
- fn test_string_complete() {
- assert_debug_snapshot!(setup(r#"@string{foo = {Hello World}}"#));
+ let group = ptr.at(ContentToken::LCurly);
+ if group {
+ ptr.expect(ContentToken::LCurly);
+ ptr.expect(ContentToken::Whitespace);
}
- #[test]
- fn test_string_incomplete() {
- assert_debug_snapshot!(setup(r#"@string{foo = {Hello World}"#));
- }
+ ptr.expect(ContentToken::Word);
- #[test]
- fn test_string_concatenation() {
- assert_debug_snapshot!(setup(
- r#"@string{foo = {Hello World}} @string{bar = foo # "!"}"#
- ));
+ if group {
+ ptr.expect(ContentToken::Whitespace);
+ ptr.expect(ContentToken::RCurly);
}
- #[test]
- fn test_string_casing() {
- assert_debug_snapshot!(setup(r#"@STRING{foo = "Hello World"}"#));
- }
+ ptr.builder.finish_node();
+ ptr
+}
- #[test]
- fn test_string_missing_quote() {
- assert_debug_snapshot!(setup(r#"@STRING{foo = "Hello World}"#));
- }
+fn command(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> {
+ ptr.builder.start_node(COMMAND.into());
+ ptr.bump();
+ ptr.builder.finish_node();
+ ptr
+}
- #[test]
- fn test_entry_no_fields() {
- assert_debug_snapshot!(setup(r#"@article{foo,}"#));
- }
+struct TokenPtr<'a, T: Logos<'a>> {
+ builder: GreenNodeBuilder<'static>,
+ lexer: Lexer<'a, T>,
+ token: Option<(T, &'a str)>,
+}
- #[test]
- fn test_entry_no_fields_missing_comma() {
- assert_debug_snapshot!(setup(r#"@article{foo}"#));
+impl<'a, T> TokenPtr<'a, T>
+where
+ T: Logos<'a, Source = str> + Eq + Copy + Into<SyntaxKind>,
+ T::Extras: Default,
+{
+ pub fn morph<U>(mut self) -> TokenPtr<'a, U>
+ where
+ U: Logos<'a, Source = str> + Eq + Copy + Into<SyntaxKind>,
+ U::Extras: Default,
+ {
+ self.peek();
+ let start = self.lexer.span().start;
+ let input = &self.lexer.source()[start..];
+ TokenPtr {
+ builder: self.builder,
+ lexer: U::lexer(input),
+ token: None,
+ }
}
- #[test]
- fn test_entry_one_field() {
- assert_debug_snapshot!(setup(r#"@article{foo, author = {Foo Bar}}"#));
+ #[must_use]
+ pub fn at(&mut self, kind: T) -> bool {
+ self.peek().map_or(false, |(k, _)| k == kind)
}
- #[test]
- fn test_entry_one_field_number_key() {
- assert_debug_snapshot!(setup(r#"@article{foo2021, author = {Foo Bar}}"#));
+ #[must_use]
+ pub fn current(&mut self) -> Option<T> {
+ self.peek().map(|(k, _)| k)
}
- #[test]
- fn test_entry_one_field_trailing_comma() {
- assert_debug_snapshot!(setup(r#"@article{foo, author = {Foo Bar},}"#));
+ pub fn bump(&mut self) {
+ let (kind, text) = self.peek().unwrap();
+ self.token = None;
+ self.builder
+ .token(rowan::SyntaxKind::from(kind.into()), text);
}
- #[test]
- fn test_entry_two_fields() {
- assert_debug_snapshot!(setup(
- r#"@article{foo, author = {Foo Bar}, title = {Hello World}}"#
- ));
+ pub fn expect(&mut self, kind: T) {
+ if self.at(kind) {
+ self.bump();
+ }
}
- #[test]
- fn test_entry_two_fields_incomplete() {
- assert_debug_snapshot!(setup(r#"@article{foo, author = {Foo Bar}, t}"#));
- }
+ fn peek(&mut self) -> Option<(T, &'a str)> {
+ if self.token.is_none() {
+ let kind = self.lexer.next()?;
+ let text = self.lexer.slice();
+ self.token = Some((kind, text));
+ }
- #[test]
- fn test_entry_complete_parens() {
- assert_debug_snapshot!(setup(
- r#"@article(foo, author = {Foo Bar}, title = {Hello})"#
- ));
+ self.token
}
}
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__empty.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__empty.snap
deleted file mode 100644
index 55e1954d2f..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__empty.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"\"#)"
-
----
-ROOT@0..0
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_complete_parens.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_complete_parens.snap
deleted file mode 100644
index d86d2fb035..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_complete_parens.snap
+++ /dev/null
@@ -1,41 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article(foo, author = {Foo Bar}, title = {Hello})\"#)"
-
----
-ROOT@0..50
- ENTRY@0..50
- ENTRY_TYPE@0..8 "@article"
- L_PAREN@8..9 "("
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- FIELD@14..32
- WORD@14..20 "author"
- WHITESPACE@20..21 " "
- EQUALITY_SIGN@21..22 "="
- WHITESPACE@22..23 " "
- VALUE@23..32
- TOKEN@23..32
- BRACE_GROUP@23..32
- L_CURLY@23..24 "{"
- WORD@24..27 "Foo"
- WHITESPACE@27..28 " "
- WORD@28..31 "Bar"
- R_CURLY@31..32 "}"
- COMMA@32..33 ","
- WHITESPACE@33..34 " "
- FIELD@34..49
- WORD@34..39 "title"
- WHITESPACE@39..40 " "
- EQUALITY_SIGN@40..41 "="
- WHITESPACE@41..42 " "
- VALUE@42..49
- TOKEN@42..49
- BRACE_GROUP@42..49
- L_CURLY@42..43 "{"
- WORD@43..48 "Hello"
- R_CURLY@48..49 "}"
- R_PAREN@49..50 ")"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields.snap
deleted file mode 100644
index e5df412782..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields.snap
+++ /dev/null
@@ -1,14 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo,}\"#)"
-
----
-ROOT@0..14
- ENTRY@0..14
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- R_CURLY@13..14 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields_missing_comma.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields_missing_comma.snap
deleted file mode 100644
index b15c871298..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_no_fields_missing_comma.snap
+++ /dev/null
@@ -1,13 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo}\"#)"
-
----
-ROOT@0..13
- ENTRY@0..13
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- R_CURLY@12..13 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field.snap
deleted file mode 100644
index 0bd43ac612..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field.snap
+++ /dev/null
@@ -1,28 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo, author = {Foo Bar}}\"#)"
-
----
-ROOT@0..33
- ENTRY@0..33
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- FIELD@14..32
- WORD@14..20 "author"
- WHITESPACE@20..21 " "
- EQUALITY_SIGN@21..22 "="
- WHITESPACE@22..23 " "
- VALUE@23..32
- TOKEN@23..32
- BRACE_GROUP@23..32
- L_CURLY@23..24 "{"
- WORD@24..27 "Foo"
- WHITESPACE@27..28 " "
- WORD@28..31 "Bar"
- R_CURLY@31..32 "}"
- R_CURLY@32..33 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_number_key.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_number_key.snap
deleted file mode 100644
index 09bb00872f..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_number_key.snap
+++ /dev/null
@@ -1,28 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo2021, author = {Foo Bar}}\"#)"
-
----
-ROOT@0..37
- ENTRY@0..37
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..16
- WORD@9..16 "foo2021"
- COMMA@16..17 ","
- WHITESPACE@17..18 " "
- FIELD@18..36
- WORD@18..24 "author"
- WHITESPACE@24..25 " "
- EQUALITY_SIGN@25..26 "="
- WHITESPACE@26..27 " "
- VALUE@27..36
- TOKEN@27..36
- BRACE_GROUP@27..36
- L_CURLY@27..28 "{"
- WORD@28..31 "Foo"
- WHITESPACE@31..32 " "
- WORD@32..35 "Bar"
- R_CURLY@35..36 "}"
- R_CURLY@36..37 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_trailing_comma.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_trailing_comma.snap
deleted file mode 100644
index 51adbbc275..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_one_field_trailing_comma.snap
+++ /dev/null
@@ -1,29 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo, author = {Foo Bar},}\"#)"
-
----
-ROOT@0..34
- ENTRY@0..34
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- FIELD@14..32
- WORD@14..20 "author"
- WHITESPACE@20..21 " "
- EQUALITY_SIGN@21..22 "="
- WHITESPACE@22..23 " "
- VALUE@23..32
- TOKEN@23..32
- BRACE_GROUP@23..32
- L_CURLY@23..24 "{"
- WORD@24..27 "Foo"
- WHITESPACE@27..28 " "
- WORD@28..31 "Bar"
- R_CURLY@31..32 "}"
- COMMA@32..33 ","
- R_CURLY@33..34 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields.snap
deleted file mode 100644
index 384d582ef9..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields.snap
+++ /dev/null
@@ -1,43 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo, author = {Foo Bar}, title = {Hello World}}\"#)"
-
----
-ROOT@0..56
- ENTRY@0..56
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- FIELD@14..32
- WORD@14..20 "author"
- WHITESPACE@20..21 " "
- EQUALITY_SIGN@21..22 "="
- WHITESPACE@22..23 " "
- VALUE@23..32
- TOKEN@23..32
- BRACE_GROUP@23..32
- L_CURLY@23..24 "{"
- WORD@24..27 "Foo"
- WHITESPACE@27..28 " "
- WORD@28..31 "Bar"
- R_CURLY@31..32 "}"
- COMMA@32..33 ","
- WHITESPACE@33..34 " "
- FIELD@34..55
- WORD@34..39 "title"
- WHITESPACE@39..40 " "
- EQUALITY_SIGN@40..41 "="
- WHITESPACE@41..42 " "
- VALUE@42..55
- TOKEN@42..55
- BRACE_GROUP@42..55
- L_CURLY@42..43 "{"
- WORD@43..48 "Hello"
- WHITESPACE@48..49 " "
- WORD@49..54 "World"
- R_CURLY@54..55 "}"
- R_CURLY@55..56 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields_incomplete.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields_incomplete.snap
deleted file mode 100644
index cbe00beff5..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__entry_two_fields_incomplete.snap
+++ /dev/null
@@ -1,34 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@article{foo, author = {Foo Bar}, t}\"#)"
-
----
-ROOT@0..36
- ENTRY@0..36
- ENTRY_TYPE@0..8 "@article"
- L_CURLY@8..9 "{"
- KEY@9..12
- WORD@9..12 "foo"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- FIELD@14..32
- WORD@14..20 "author"
- WHITESPACE@20..21 " "
- EQUALITY_SIGN@21..22 "="
- WHITESPACE@22..23 " "
- VALUE@23..32
- TOKEN@23..32
- BRACE_GROUP@23..32
- L_CURLY@23..24 "{"
- WORD@24..27 "Foo"
- WHITESPACE@27..28 " "
- WORD@28..31 "Bar"
- R_CURLY@31..32 "}"
- COMMA@32..33 ","
- WHITESPACE@33..34 " "
- FIELD@34..35
- WORD@34..35 "t"
- MISSING@35..35 ""
- MISSING@35..35 ""
- R_CURLY@35..36 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__junk.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__junk.snap
deleted file mode 100644
index e77313cabf..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__junk.snap
+++ /dev/null
@@ -1,11 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"Hello World!\"#)"
-
----
-ROOT@0..12
- JUNK@0..12
- WORD@0..5 "Hello"
- WHITESPACE@5..6 " "
- WORD@6..12 "World!"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_casing.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_casing.snap
deleted file mode 100644
index f4669b8a01..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_casing.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@preAmbLe{ \"Hello World\" }\"#)"
-
----
-ROOT@0..26
- PREAMBLE@0..26
- PREAMBLE_TYPE@0..9 "@preAmbLe"
- L_CURLY@9..10 "{"
- WHITESPACE@10..11 " "
- VALUE@11..25
- TOKEN@11..25
- QUOTE_GROUP@11..25
- QUOTE@11..12 "\""
- WORD@12..17 "Hello"
- WHITESPACE@17..18 " "
- WORD@18..23 "World"
- QUOTE@23..24 "\""
- WHITESPACE@24..25 " "
- R_CURLY@25..26 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_complete.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_complete.snap
deleted file mode 100644
index d1977f2e76..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_complete.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@preamble{ \"Hello World\" }\"#)"
-
----
-ROOT@0..26
- PREAMBLE@0..26
- PREAMBLE_TYPE@0..9 "@preamble"
- L_CURLY@9..10 "{"
- WHITESPACE@10..11 " "
- VALUE@11..25
- TOKEN@11..25
- QUOTE_GROUP@11..25
- QUOTE@11..12 "\""
- WORD@12..17 "Hello"
- WHITESPACE@17..18 " "
- WORD@18..23 "World"
- QUOTE@23..24 "\""
- WHITESPACE@24..25 " "
- R_CURLY@25..26 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_missing_end.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_missing_end.snap
deleted file mode 100644
index 4ba447a2ae..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__preamble_missing_end.snap
+++ /dev/null
@@ -1,20 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@preamble{ \"Hello World\" \"#)"
-
----
-ROOT@0..24
- PREAMBLE@0..24
- PREAMBLE_TYPE@0..9 "@preamble"
- L_CURLY@9..10 "{"
- WHITESPACE@10..11 " "
- VALUE@11..24
- TOKEN@11..24
- QUOTE_GROUP@11..24
- QUOTE@11..12 "\""
- WORD@12..17 "Hello"
- WHITESPACE@17..18 " "
- WORD@18..23 "World"
- QUOTE@23..24 "\""
- MISSING@24..24 ""
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_casing.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_casing.snap
deleted file mode 100644
index 1c8b1dbea4..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_casing.snap
+++ /dev/null
@@ -1,23 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@STRING{foo = \"Hello World\"}\"#)"
-
----
-ROOT@0..28
- STRING@0..28
- STRING_TYPE@0..7 "@STRING"
- L_CURLY@7..8 "{"
- WORD@8..11 "foo"
- WHITESPACE@11..12 " "
- EQUALITY_SIGN@12..13 "="
- WHITESPACE@13..14 " "
- VALUE@14..27
- TOKEN@14..27
- QUOTE_GROUP@14..27
- QUOTE@14..15 "\""
- WORD@15..20 "Hello"
- WHITESPACE@20..21 " "
- WORD@21..26 "World"
- QUOTE@26..27 "\""
- R_CURLY@27..28 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_complete.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_complete.snap
deleted file mode 100644
index 506452894d..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_complete.snap
+++ /dev/null
@@ -1,23 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@string{foo = {Hello World}}\"#)"
-
----
-ROOT@0..28
- STRING@0..28
- STRING_TYPE@0..7 "@string"
- L_CURLY@7..8 "{"
- WORD@8..11 "foo"
- WHITESPACE@11..12 " "
- EQUALITY_SIGN@12..13 "="
- WHITESPACE@13..14 " "
- VALUE@14..27
- TOKEN@14..27
- BRACE_GROUP@14..27
- L_CURLY@14..15 "{"
- WORD@15..20 "Hello"
- WHITESPACE@20..21 " "
- WORD@21..26 "World"
- R_CURLY@26..27 "}"
- R_CURLY@27..28 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_concatenation.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_concatenation.snap
deleted file mode 100644
index 7f457d6964..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_concatenation.snap
+++ /dev/null
@@ -1,43 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@string{foo = {Hello World}} @string{bar = foo # \"!\"}\"#)"
-
----
-ROOT@0..53
- STRING@0..29
- STRING_TYPE@0..7 "@string"
- L_CURLY@7..8 "{"
- WORD@8..11 "foo"
- WHITESPACE@11..12 " "
- EQUALITY_SIGN@12..13 "="
- WHITESPACE@13..14 " "
- VALUE@14..27
- TOKEN@14..27
- BRACE_GROUP@14..27
- L_CURLY@14..15 "{"
- WORD@15..20 "Hello"
- WHITESPACE@20..21 " "
- WORD@21..26 "World"
- R_CURLY@26..27 "}"
- R_CURLY@27..28 "}"
- WHITESPACE@28..29 " "
- STRING@29..53
- STRING_TYPE@29..36 "@string"
- L_CURLY@36..37 "{"
- WORD@37..40 "bar"
- WHITESPACE@40..41 " "
- EQUALITY_SIGN@41..42 "="
- WHITESPACE@42..43 " "
- VALUE@43..52
- TOKEN@43..46
- WORD@43..46 "foo"
- WHITESPACE@46..47 " "
- HASH@47..48 "#"
- WHITESPACE@48..49 " "
- TOKEN@49..52
- QUOTE_GROUP@49..52
- QUOTE@49..50 "\""
- WORD@50..51 "!"
- QUOTE@51..52 "\""
- R_CURLY@52..53 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_incomplete.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_incomplete.snap
deleted file mode 100644
index 685d02a179..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_incomplete.snap
+++ /dev/null
@@ -1,23 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@string{foo = {Hello World}\"#)"
-
----
-ROOT@0..27
- STRING@0..27
- STRING_TYPE@0..7 "@string"
- L_CURLY@7..8 "{"
- WORD@8..11 "foo"
- WHITESPACE@11..12 " "
- EQUALITY_SIGN@12..13 "="
- WHITESPACE@13..14 " "
- VALUE@14..27
- TOKEN@14..27
- BRACE_GROUP@14..27
- L_CURLY@14..15 "{"
- WORD@15..20 "Hello"
- WHITESPACE@20..21 " "
- WORD@21..26 "World"
- R_CURLY@26..27 "}"
- MISSING@27..27 ""
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_missing_quote.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_missing_quote.snap
deleted file mode 100644
index 8eeefa9bf9..0000000000
--- a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__parser__tests__string_missing_quote.snap
+++ /dev/null
@@ -1,23 +0,0 @@
----
-source: src/syntax/bibtex/parser.rs
-expression: "setup(r#\"@STRING{foo = \"Hello World}\"#)"
-
----
-ROOT@0..27
- STRING@0..27
- STRING_TYPE@0..7 "@STRING"
- L_CURLY@7..8 "{"
- WORD@8..11 "foo"
- WHITESPACE@11..12 " "
- EQUALITY_SIGN@12..13 "="
- WHITESPACE@13..14 " "
- VALUE@14..26
- TOKEN@14..26
- QUOTE_GROUP@14..26
- QUOTE@14..15 "\""
- WORD@15..20 "Hello"
- WHITESPACE@20..21 " "
- WORD@21..26 "World"
- MISSING@26..26 ""
- R_CURLY@26..27 "}"
-
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_001.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_001.snap
new file mode 100644
index 0000000000..4fbec161d2
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_001.snap
@@ -0,0 +1,430 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 47
+expression: "setup(r#\"@incollection{westfahl:space,\n author = {Westfahl, Gary},\n title = {The True Frontier},\n subtitle = {Confronting and Avoiding the Realities of Space in {American}\n Science Fiction Films},\n pages = {55-65},\n crossref = {westfahl:frontier},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {True Frontier, The},\n annotation = {A cross-referenced article from a \\texttt{collection}. This is\n an \\texttt{incollection} entry with a \\texttt{crossref}\n field. Note the \\texttt{subtitle} and \\texttt{indextitle}\n fields},\n}\n\n% booktitle and booksubtitle are only needed for BibTeX's less sophisticated\n% inheritance set-up to make sure westfahl:space shows correctly.\n% With Biber they are not needed.\n@collection{westfahl:frontier,\n editor = {Westfahl, Gary},\n title = {Space and Beyond},\n date = 2000,\n subtitle = {The Frontier Theme in Science Fiction},\n publisher = {Greenwood},\n location = {Westport, Conn. and London},\n langid = {english},\n langidopts = {variant=american},\n booktitle = {Space and Beyond},\n booksubtitle = {The Frontier Theme in Science Fiction},\n annotation = {This is a \\texttt{collection} entry. Note the format of the\n \\texttt{location} field as well as the \\texttt{subtitle}\n field},\n}\"#)"
+---
+ROOT@0..1484
+ ENTRY@0..678
+ TYPE@0..13 "@incollection"
+ L_DELIM@13..14 "{"
+ NAME@14..28 "westfahl:space"
+ COMMA@28..29 ","
+ WHITESPACE@29..34 "\n "
+ FIELD@34..66
+ NAME@34..40 "author"
+ WHITESPACE@40..47 " "
+ EQ@47..48 "="
+ WHITESPACE@48..49 " "
+ CURLY_GROUP@49..65
+ L_CURLY@49..50 "{"
+ WORD@50..58 "Westfahl"
+ COMMA@58..59 ","
+ WHITESPACE@59..60 " "
+ WORD@60..64 "Gary"
+ R_CURLY@64..65 "}"
+ COMMA@65..66 ","
+ WHITESPACE@66..71 "\n "
+ FIELD@71..106
+ NAME@71..76 "title"
+ WHITESPACE@76..84 " "
+ EQ@84..85 "="
+ WHITESPACE@85..86 " "
+ CURLY_GROUP@86..105
+ L_CURLY@86..87 "{"
+ WORD@87..90 "The"
+ WHITESPACE@90..91 " "
+ WORD@91..95 "True"
+ WHITESPACE@95..96 " "
+ WORD@96..104 "Frontier"
+ R_CURLY@104..105 "}"
+ COMMA@105..106 ","
+ WHITESPACE@106..111 "\n "
+ FIELD@111..232
+ NAME@111..119 "subtitle"
+ WHITESPACE@119..124 " "
+ EQ@124..125 "="
+ WHITESPACE@125..126 " "
+ CURLY_GROUP@126..231
+ L_CURLY@126..127 "{"
+ WORD@127..138 "Confronting"
+ WHITESPACE@138..139 " "
+ WORD@139..142 "and"
+ WHITESPACE@142..143 " "
+ WORD@143..151 "Avoiding"
+ WHITESPACE@151..152 " "
+ WORD@152..155 "the"
+ WHITESPACE@155..156 " "
+ WORD@156..165 "Realities"
+ WHITESPACE@165..166 " "
+ WORD@166..168 "of"
+ WHITESPACE@168..169 " "
+ WORD@169..174 "Space"
+ WHITESPACE@174..175 " "
+ WORD@175..177 "in"
+ WHITESPACE@177..178 " "
+ CURLY_GROUP@178..188
+ L_CURLY@178..179 "{"
+ WORD@179..187 "American"
+ R_CURLY@187..188 "}"
+ WHITESPACE@188..209 "\n "
+ WORD@209..216 "Science"
+ WHITESPACE@216..217 " "
+ WORD@217..224 "Fiction"
+ WHITESPACE@224..225 " "
+ WORD@225..230 "Films"
+ R_CURLY@230..231 "}"
+ COMMA@231..232 ","
+ WHITESPACE@232..237 "\n "
+ FIELD@237..260
+ NAME@237..242 "pages"
+ WHITESPACE@242..250 " "
+ EQ@250..251 "="
+ WHITESPACE@251..252 " "
+ CURLY_GROUP@252..259
+ L_CURLY@252..253 "{"
+ WORD@253..258 "55-65"
+ R_CURLY@258..259 "}"
+ COMMA@259..260 ","
+ WHITESPACE@260..265 "\n "
+ FIELD@265..300
+ NAME@265..273 "crossref"
+ WHITESPACE@273..278 " "
+ EQ@278..279 "="
+ WHITESPACE@279..280 " "
+ CURLY_GROUP@280..299
+ L_CURLY@280..281 "{"
+ WORD@281..298 "westfahl:frontier"
+ R_CURLY@298..299 "}"
+ COMMA@299..300 ","
+ WHITESPACE@300..305 "\n "
+ FIELD@305..330
+ NAME@305..311 "langid"
+ WHITESPACE@311..318 " "
+ EQ@318..319 "="
+ WHITESPACE@319..320 " "
+ CURLY_GROUP@320..329
+ L_CURLY@320..321 "{"
+ WORD@321..328 "english"
+ R_CURLY@328..329 "}"
+ COMMA@329..330 ","
+ WHITESPACE@330..335 "\n "
+ FIELD@335..369
+ NAME@335..345 "langidopts"
+ WHITESPACE@345..348 " "
+ EQ@348..349 "="
+ WHITESPACE@349..350 " "
+ CURLY_GROUP@350..368
+ L_CURLY@350..351 "{"
+ WORD@351..367 "variant=american"
+ R_CURLY@367..368 "}"
+ COMMA@368..369 ","
+ WHITESPACE@369..374 "\n "
+ FIELD@374..410
+ NAME@374..384 "indextitle"
+ WHITESPACE@384..387 " "
+ EQ@387..388 "="
+ WHITESPACE@388..389 " "
+ CURLY_GROUP@389..409
+ L_CURLY@389..390 "{"
+ WORD@390..394 "True"
+ WHITESPACE@394..395 " "
+ WORD@395..403 "Frontier"
+ COMMA@403..404 ","
+ WHITESPACE@404..405 " "
+ WORD@405..408 "The"
+ R_CURLY@408..409 "}"
+ COMMA@409..410 ","
+ WHITESPACE@410..415 "\n "
+ FIELD@415..676
+ NAME@415..425 "annotation"
+ WHITESPACE@425..428 " "
+ EQ@428..429 "="
+ WHITESPACE@429..430 " "
+ CURLY_GROUP@430..675
+ L_CURLY@430..431 "{"
+ WORD@431..432 "A"
+ WHITESPACE@432..433 " "
+ WORD@433..449 "cross-referenced"
+ WHITESPACE@449..450 " "
+ WORD@450..457 "article"
+ WHITESPACE@457..458 " "
+ WORD@458..462 "from"
+ WHITESPACE@462..463 " "
+ WORD@463..464 "a"
+ WHITESPACE@464..465 " "
+ COMMAND@465..472
+ COMMAND_NAME@465..472 "\\texttt"
+ CURLY_GROUP@472..484
+ L_CURLY@472..473 "{"
+ WORD@473..483 "collection"
+ R_CURLY@483..484 "}"
+ WORD@484..485 "."
+ WHITESPACE@485..486 " "
+ WORD@486..490 "This"
+ WHITESPACE@490..491 " "
+ WORD@491..493 "is"
+ WHITESPACE@493..514 "\n "
+ WORD@514..516 "an"
+ WHITESPACE@516..517 " "
+ COMMAND@517..524
+ COMMAND_NAME@517..524 "\\texttt"
+ CURLY_GROUP@524..538
+ L_CURLY@524..525 "{"
+ WORD@525..537 "incollection"
+ R_CURLY@537..538 "}"
+ WHITESPACE@538..539 " "
+ WORD@539..544 "entry"
+ WHITESPACE@544..545 " "
+ WORD@545..549 "with"
+ WHITESPACE@549..550 " "
+ WORD@550..551 "a"
+ WHITESPACE@551..552 " "
+ COMMAND@552..559
+ COMMAND_NAME@552..559 "\\texttt"
+ CURLY_GROUP@559..569
+ L_CURLY@559..560 "{"
+ WORD@560..568 "crossref"
+ R_CURLY@568..569 "}"
+ WHITESPACE@569..590 "\n "
+ WORD@590..596 "field."
+ WHITESPACE@596..597 " "
+ WORD@597..601 "Note"
+ WHITESPACE@601..602 " "
+ WORD@602..605 "the"
+ WHITESPACE@605..606 " "
+ COMMAND@606..613
+ COMMAND_NAME@606..613 "\\texttt"
+ CURLY_GROUP@613..623
+ L_CURLY@613..614 "{"
+ WORD@614..622 "subtitle"
+ R_CURLY@622..623 "}"
+ WHITESPACE@623..624 " "
+ WORD@624..627 "and"
+ WHITESPACE@627..628 " "
+ COMMAND@628..635
+ COMMAND_NAME@628..635 "\\texttt"
+ CURLY_GROUP@635..647
+ L_CURLY@635..636 "{"
+ WORD@636..646 "indextitle"
+ R_CURLY@646..647 "}"
+ WHITESPACE@647..668 "\n "
+ WORD@668..674 "fields"
+ R_CURLY@674..675 "}"
+ COMMA@675..676 ","
+ WHITESPACE@676..677 "\n"
+ R_DELIM@677..678 "}"
+ JUNK@678..857 "\n\n% booktitle and boo ..."
+ ENTRY@857..1484
+ TYPE@857..868 "@collection"
+ L_DELIM@868..869 "{"
+ NAME@869..886 "westfahl:frontier"
+ COMMA@886..887 ","
+ WHITESPACE@887..892 "\n "
+ FIELD@892..924
+ NAME@892..898 "editor"
+ WHITESPACE@898..905 " "
+ EQ@905..906 "="
+ WHITESPACE@906..907 " "
+ CURLY_GROUP@907..923
+ L_CURLY@907..908 "{"
+ WORD@908..916 "Westfahl"
+ COMMA@916..917 ","
+ WHITESPACE@917..918 " "
+ WORD@918..922 "Gary"
+ R_CURLY@922..923 "}"
+ COMMA@923..924 ","
+ WHITESPACE@924..929 "\n "
+ FIELD@929..963
+ NAME@929..934 "title"
+ WHITESPACE@934..942 " "
+ EQ@942..943 "="
+ WHITESPACE@943..944 " "
+ CURLY_GROUP@944..962
+ L_CURLY@944..945 "{"
+ WORD@945..950 "Space"
+ WHITESPACE@950..951 " "
+ WORD@951..954 "and"
+ WHITESPACE@954..955 " "
+ WORD@955..961 "Beyond"
+ R_CURLY@961..962 "}"
+ COMMA@962..963 ","
+ WHITESPACE@963..968 "\n "
+ FIELD@968..988
+ NAME@968..972 "date"
+ WHITESPACE@972..981 " "
+ EQ@981..982 "="
+ WHITESPACE@982..983 " "
+ LITERAL@983..987
+ INTEGER@983..987 "2000"
+ COMMA@987..988 ","
+ WHITESPACE@988..993 "\n "
+ FIELD@993..1048
+ NAME@993..1001 "subtitle"
+ WHITESPACE@1001..1006 " "
+ EQ@1006..1007 "="
+ WHITESPACE@1007..1008 " "
+ CURLY_GROUP@1008..1047
+ L_CURLY@1008..1009 "{"
+ WORD@1009..1012 "The"
+ WHITESPACE@1012..1013 " "
+ WORD@1013..1021 "Frontier"
+ WHITESPACE@1021..1022 " "
+ WORD@1022..1027 "Theme"
+ WHITESPACE@1027..1028 " "
+ WORD@1028..1030 "in"
+ WHITESPACE@1030..1031 " "
+ WORD@1031..1038 "Science"
+ WHITESPACE@1038..1039 " "
+ WORD@1039..1046 "Fiction"
+ R_CURLY@1046..1047 "}"
+ COMMA@1047..1048 ","
+ WHITESPACE@1048..1053 "\n "
+ FIELD@1053..1080
+ NAME@1053..1062 "publisher"
+ WHITESPACE@1062..1066 " "
+ EQ@1066..1067 "="
+ WHITESPACE@1067..1068 " "
+ CURLY_GROUP@1068..1079
+ L_CURLY@1068..1069 "{"
+ WORD@1069..1078 "Greenwood"
+ R_CURLY@1078..1079 "}"
+ COMMA@1079..1080 ","
+ WHITESPACE@1080..1085 "\n "
+ FIELD@1085..1129
+ NAME@1085..1093 "location"
+ WHITESPACE@1093..1098 " "
+ EQ@1098..1099 "="
+ WHITESPACE@1099..1100 " "
+ CURLY_GROUP@1100..1128
+ L_CURLY@1100..1101 "{"
+ WORD@1101..1109 "Westport"
+ COMMA@1109..1110 ","
+ WHITESPACE@1110..1111 " "
+ WORD@1111..1116 "Conn."
+ WHITESPACE@1116..1117 " "
+ WORD@1117..1120 "and"
+ WHITESPACE@1120..1121 " "
+ WORD@1121..1127 "London"
+ R_CURLY@1127..1128 "}"
+ COMMA@1128..1129 ","
+ WHITESPACE@1129..1134 "\n "
+ FIELD@1134..1159
+ NAME@1134..1140 "langid"
+ WHITESPACE@1140..1147 " "
+ EQ@1147..1148 "="
+ WHITESPACE@1148..1149 " "
+ CURLY_GROUP@1149..1158
+ L_CURLY@1149..1150 "{"
+ WORD@1150..1157 "english"
+ R_CURLY@1157..1158 "}"
+ COMMA@1158..1159 ","
+ WHITESPACE@1159..1164 "\n "
+ FIELD@1164..1198
+ NAME@1164..1174 "langidopts"
+ WHITESPACE@1174..1177 " "
+ EQ@1177..1178 "="
+ WHITESPACE@1178..1179 " "
+ CURLY_GROUP@1179..1197
+ L_CURLY@1179..1180 "{"
+ WORD@1180..1196 "variant=american"
+ R_CURLY@1196..1197 "}"
+ COMMA@1197..1198 ","
+ WHITESPACE@1198..1203 "\n "
+ FIELD@1203..1237
+ NAME@1203..1212 "booktitle"
+ WHITESPACE@1212..1216 " "
+ EQ@1216..1217 "="
+ WHITESPACE@1217..1218 " "
+ CURLY_GROUP@1218..1236
+ L_CURLY@1218..1219 "{"
+ WORD@1219..1224 "Space"
+ WHITESPACE@1224..1225 " "
+ WORD@1225..1228 "and"
+ WHITESPACE@1228..1229 " "
+ WORD@1229..1235 "Beyond"
+ R_CURLY@1235..1236 "}"
+ COMMA@1236..1237 ","
+ WHITESPACE@1237..1242 "\n "
+ FIELD@1242..1297
+ NAME@1242..1254 "booksubtitle"
+ WHITESPACE@1254..1255 " "
+ EQ@1255..1256 "="
+ WHITESPACE@1256..1257 " "
+ CURLY_GROUP@1257..1296
+ L_CURLY@1257..1258 "{"
+ WORD@1258..1261 "The"
+ WHITESPACE@1261..1262 " "
+ WORD@1262..1270 "Frontier"
+ WHITESPACE@1270..1271 " "
+ WORD@1271..1276 "Theme"
+ WHITESPACE@1276..1277 " "
+ WORD@1277..1279 "in"
+ WHITESPACE@1279..1280 " "
+ WORD@1280..1287 "Science"
+ WHITESPACE@1287..1288 " "
+ WORD@1288..1295 "Fiction"
+ R_CURLY@1295..1296 "}"
+ COMMA@1296..1297 ","
+ WHITESPACE@1297..1302 "\n "
+ FIELD@1302..1482
+ NAME@1302..1312 "annotation"
+ WHITESPACE@1312..1315 " "
+ EQ@1315..1316 "="
+ WHITESPACE@1316..1317 " "
+ CURLY_GROUP@1317..1481
+ L_CURLY@1317..1318 "{"
+ WORD@1318..1322 "This"
+ WHITESPACE@1322..1323 " "
+ WORD@1323..1325 "is"
+ WHITESPACE@1325..1326 " "
+ WORD@1326..1327 "a"
+ WHITESPACE@1327..1328 " "
+ COMMAND@1328..1335
+ COMMAND_NAME@1328..1335 "\\texttt"
+ CURLY_GROUP@1335..1347
+ L_CURLY@1335..1336 "{"
+ WORD@1336..1346 "collection"
+ R_CURLY@1346..1347 "}"
+ WHITESPACE@1347..1348 " "
+ WORD@1348..1354 "entry."
+ WHITESPACE@1354..1355 " "
+ WORD@1355..1359 "Note"
+ WHITESPACE@1359..1360 " "
+ WORD@1360..1363 "the"
+ WHITESPACE@1363..1364 " "
+ WORD@1364..1370 "format"
+ WHITESPACE@1370..1371 " "
+ WORD@1371..1373 "of"
+ WHITESPACE@1373..1374 " "
+ WORD@1374..1377 "the"
+ WHITESPACE@1377..1398 "\n "
+ COMMAND@1398..1405
+ COMMAND_NAME@1398..1405 "\\texttt"
+ CURLY_GROUP@1405..1415
+ L_CURLY@1405..1406 "{"
+ WORD@1406..1414 "location"
+ R_CURLY@1414..1415 "}"
+ WHITESPACE@1415..1416 " "
+ WORD@1416..1421 "field"
+ WHITESPACE@1421..1422 " "
+ WORD@1422..1424 "as"
+ WHITESPACE@1424..1425 " "
+ WORD@1425..1429 "well"
+ WHITESPACE@1429..1430 " "
+ WORD@1430..1432 "as"
+ WHITESPACE@1432..1433 " "
+ WORD@1433..1436 "the"
+ WHITESPACE@1436..1437 " "
+ COMMAND@1437..1444
+ COMMAND_NAME@1437..1444 "\\texttt"
+ CURLY_GROUP@1444..1454
+ L_CURLY@1444..1445 "{"
+ WORD@1445..1453 "subtitle"
+ R_CURLY@1453..1454 "}"
+ WHITESPACE@1454..1475 "\n "
+ WORD@1475..1480 "field"
+ R_CURLY@1480..1481 "}"
+ COMMA@1481..1482 ","
+ WHITESPACE@1482..1483 "\n"
+ R_DELIM@1483..1484 "}"
+
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_002.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_002.snap
new file mode 100644
index 0000000000..5607e5a507
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_002.snap
@@ -0,0 +1,258 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 87
+expression: "setup(r#\"@string{jomch = {J.~Organomet. Chem.}}\n\n@article{aksin,\n author = {Aks{\\i}n, {\\\"O}zge and T{\\\"u}rkmen, Hayati and Artok, Levent\n and {\\c{C}}etinkaya, Bekir and Ni, Chaoying and\n B{\\\"u}y{\\\"u}kg{\\\"u}ng{\\\"o}r, Orhan and {\\\"O}zkal, Erhan},\n title = {Effect of immobilization on catalytic characteristics of\n saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}\n reactions},\n journaltitle = jomch,\n date = 2006,\n volume = 691,\n number = 13,\n pages = {3027-3036},\n indextitle = {Effect of immobilization on catalytic characteristics},\n}\"#)"
+---
+ROOT@0..679
+ STRING@0..40
+ TYPE@0..7 "@string"
+ L_DELIM@7..8 "{"
+ NAME@8..13 "jomch"
+ WHITESPACE@13..16 " "
+ EQ@16..17 "="
+ WHITESPACE@17..18 " "
+ CURLY_GROUP@18..39
+ L_CURLY@18..19 "{"
+ WORD@19..21 "J."
+ NBSP@21..22 "~"
+ WORD@22..32 "Organomet."
+ WHITESPACE@32..33 " "
+ WORD@33..38 "Chem."
+ R_CURLY@38..39 "}"
+ R_DELIM@39..40 "}"
+ JUNK@40..42 "\n\n"
+ ENTRY@42..679
+ TYPE@42..50 "@article"
+ L_DELIM@50..51 "{"
+ NAME@51..56 "aksin"
+ COMMA@56..57 ","
+ WHITESPACE@57..62 "\n "
+ FIELD@62..284
+ NAME@62..68 "author"
+ WHITESPACE@68..75 " "
+ EQ@75..76 "="
+ WHITESPACE@76..77 " "
+ CURLY_GROUP@77..283
+ L_CURLY@77..78 "{"
+ WORD@78..81 "Aks"
+ CURLY_GROUP@81..85
+ L_CURLY@81..82 "{"
+ COMMAND@82..84
+ COMMAND_NAME@82..84 "\\i"
+ R_CURLY@84..85 "}"
+ WORD@85..86 "n"
+ COMMA@86..87 ","
+ WHITESPACE@87..88 " "
+ CURLY_GROUP@88..93
+ L_CURLY@88..89 "{"
+ ACCENT@89..92
+ ACCENT_NAME@89..91 "\\\""
+ WORD@91..92 "O"
+ R_CURLY@92..93 "}"
+ WORD@93..96 "zge"
+ WHITESPACE@96..97 " "
+ WORD@97..100 "and"
+ WHITESPACE@100..101 " "
+ WORD@101..102 "T"
+ CURLY_GROUP@102..107
+ L_CURLY@102..103 "{"
+ ACCENT@103..106
+ ACCENT_NAME@103..105 "\\\""
+ WORD@105..106 "u"
+ R_CURLY@106..107 "}"
+ WORD@107..112 "rkmen"
+ COMMA@112..113 ","
+ WHITESPACE@113..114 " "
+ WORD@114..120 "Hayati"
+ WHITESPACE@120..121 " "
+ WORD@121..124 "and"
+ WHITESPACE@124..125 " "
+ WORD@125..130 "Artok"
+ COMMA@130..131 ","
+ WHITESPACE@131..132 " "
+ WORD@132..138 "Levent"
+ WHITESPACE@138..159 "\n "
+ WORD@159..162 "and"
+ WHITESPACE@162..163 " "
+ CURLY_GROUP@163..170
+ L_CURLY@163..164 "{"
+ ACCENT@164..169
+ ACCENT_NAME@164..166 "\\c"
+ L_CURLY@166..167 "{"
+ WORD@167..168 "C"
+ R_CURLY@168..169 "}"
+ R_CURLY@169..170 "}"
+ WORD@170..178 "etinkaya"
+ COMMA@178..179 ","
+ WHITESPACE@179..180 " "
+ WORD@180..185 "Bekir"
+ WHITESPACE@185..186 " "
+ WORD@186..189 "and"
+ WHITESPACE@189..190 " "
+ WORD@190..192 "Ni"
+ COMMA@192..193 ","
+ WHITESPACE@193..194 " "
+ WORD@194..202 "Chaoying"
+ WHITESPACE@202..203 " "
+ WORD@203..206 "and"
+ WHITESPACE@206..227 "\n "
+ WORD@227..228 "B"
+ CURLY_GROUP@228..233
+ L_CURLY@228..229 "{"
+ ACCENT@229..232
+ ACCENT_NAME@229..231 "\\\""
+ WORD@231..232 "u"
+ R_CURLY@232..233 "}"
+ WORD@233..234 "y"
+ CURLY_GROUP@234..239
+ L_CURLY@234..235 "{"
+ ACCENT@235..238
+ ACCENT_NAME@235..237 "\\\""
+ WORD@237..238 "u"
+ R_CURLY@238..239 "}"
+ WORD@239..241 "kg"
+ CURLY_GROUP@241..246
+ L_CURLY@241..242 "{"
+ ACCENT@242..245
+ ACCENT_NAME@242..244 "\\\""
+ WORD@244..245 "u"
+ R_CURLY@245..246 "}"
+ WORD@246..248 "ng"
+ CURLY_GROUP@248..253
+ L_CURLY@248..249 "{"
+ ACCENT@249..252
+ ACCENT_NAME@249..251 "\\\""
+ WORD@251..252 "o"
+ R_CURLY@252..253 "}"
+ WORD@253..254 "r"
+ COMMA@254..255 ","
+ WHITESPACE@255..256 " "
+ WORD@256..261 "Orhan"
+ WHITESPACE@261..262 " "
+ WORD@262..265 "and"
+ WHITESPACE@265..266 " "
+ CURLY_GROUP@266..271
+ L_CURLY@266..267 "{"
+ ACCENT@267..270
+ ACCENT_NAME@267..269 "\\\""
+ WORD@269..270 "O"
+ R_CURLY@270..271 "}"
+ WORD@271..275 "zkal"
+ COMMA@275..276 ","
+ WHITESPACE@276..277 " "
+ WORD@277..282 "Erhan"
+ R_CURLY@282..283 "}"
+ COMMA@283..284 ","
+ WHITESPACE@284..289 "\n "
+ FIELD@289..471
+ NAME@289..294 "title"
+ WHITESPACE@294..302 " "
+ EQ@302..303 "="
+ WHITESPACE@303..304 " "
+ CURLY_GROUP@304..470
+ L_CURLY@304..305 "{"
+ WORD@305..311 "Effect"
+ WHITESPACE@311..312 " "
+ WORD@312..314 "of"
+ WHITESPACE@314..315 " "
+ WORD@315..329 "immobilization"
+ WHITESPACE@329..330 " "
+ WORD@330..332 "on"
+ WHITESPACE@332..333 " "
+ WORD@333..342 "catalytic"
+ WHITESPACE@342..343 " "
+ WORD@343..358 "characteristics"
+ WHITESPACE@358..359 " "
+ WORD@359..361 "of"
+ WHITESPACE@361..382 "\n "
+ WORD@382..391 "saturated"
+ WHITESPACE@391..392 " "
+ CURLY_GROUP@392..398
+ L_CURLY@392..393 "{"
+ WORD@393..397 "Pd-N"
+ R_CURLY@397..398 "}"
+ WORD@398..411 "-heterocyclic"
+ WHITESPACE@411..412 " "
+ WORD@412..420 "carbenes"
+ WHITESPACE@420..421 " "
+ WORD@421..423 "in"
+ WHITESPACE@423..424 " "
+ CURLY_GROUP@424..439
+ L_CURLY@424..425 "{"
+ WORD@425..438 "Mizoroki-Heck"
+ R_CURLY@438..439 "}"
+ WHITESPACE@439..460 "\n "
+ WORD@460..469 "reactions"
+ R_CURLY@469..470 "}"
+ COMMA@470..471 ","
+ WHITESPACE@471..476 "\n "
+ FIELD@476..497
+ NAME@476..488 "journaltitle"
+ WHITESPACE@488..489 " "
+ EQ@489..490 "="
+ WHITESPACE@490..491 " "
+ LITERAL@491..496
+ NAME@491..496 "jomch"
+ COMMA@496..497 ","
+ WHITESPACE@497..502 "\n "
+ FIELD@502..522
+ NAME@502..506 "date"
+ WHITESPACE@506..515 " "
+ EQ@515..516 "="
+ WHITESPACE@516..517 " "
+ LITERAL@517..521
+ INTEGER@517..521 "2006"
+ COMMA@521..522 ","
+ WHITESPACE@522..527 "\n "
+ FIELD@527..546
+ NAME@527..533 "volume"
+ WHITESPACE@533..540 " "
+ EQ@540..541 "="
+ WHITESPACE@541..542 " "
+ LITERAL@542..545
+ INTEGER@542..545 "691"
+ COMMA@545..546 ","
+ WHITESPACE@546..551 "\n "
+ FIELD@551..569
+ NAME@551..557 "number"
+ WHITESPACE@557..564 " "
+ EQ@564..565 "="
+ WHITESPACE@565..566 " "
+ LITERAL@566..568
+ INTEGER@566..568 "13"
+ COMMA@568..569 ","
+ WHITESPACE@569..574 "\n "
+ FIELD@574..601
+ NAME@574..579 "pages"
+ WHITESPACE@579..587 " "
+ EQ@587..588 "="
+ WHITESPACE@588..589 " "
+ CURLY_GROUP@589..600
+ L_CURLY@589..590 "{"
+ WORD@590..599 "3027-3036"
+ R_CURLY@599..600 "}"
+ COMMA@600..601 ","
+ WHITESPACE@601..606 "\n "
+ FIELD@606..677
+ NAME@606..616 "indextitle"
+ WHITESPACE@616..619 " "
+ EQ@619..620 "="
+ WHITESPACE@620..621 " "
+ CURLY_GROUP@621..676
+ L_CURLY@621..622 "{"
+ WORD@622..628 "Effect"
+ WHITESPACE@628..629 " "
+ WORD@629..631 "of"
+ WHITESPACE@631..632 " "
+ WORD@632..646 "immobilization"
+ WHITESPACE@646..647 " "
+ WORD@647..649 "on"
+ WHITESPACE@649..650 " "
+ WORD@650..659 "catalytic"
+ WHITESPACE@659..660 " "
+ WORD@660..675 "characteristics"
+ R_CURLY@675..676 "}"
+ COMMA@676..677 ","
+ WHITESPACE@677..678 "\n"
+ R_DELIM@678..679 "}"
+
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_003.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_003.snap
new file mode 100644
index 0000000000..360b133a8e
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__biblatex_examples_003.snap
@@ -0,0 +1,208 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 109
+expression: "setup(r#\"@article{angenendt,\n author = {Angenendt, Arnold},\n title = {In Honore Salvatoris~-- Vom Sinn und Unsinn der\n Patrozinienkunde},\n journaltitle = {Revue d'Histoire Eccl{\\'e}siastique},\n date = 2002,\n volume = 97,\n pages = {431--456, 791--823},\n langid = {german},\n indextitle = {In Honore Salvatoris},\n shorttitle = {In Honore Salvatoris},\n annotation = {A German article in a French journal. Apart from that, a\n typical \\texttt{article} entry. Note the \\texttt{indextitle}\n field},\n}\"#)"
+---
+ROOT@0..616
+ ENTRY@0..616
+ TYPE@0..8 "@article"
+ L_DELIM@8..9 "{"
+ NAME@9..18 "angenendt"
+ COMMA@18..19 ","
+ WHITESPACE@19..24 "\n "
+ FIELD@24..59
+ NAME@24..30 "author"
+ WHITESPACE@30..37 " "
+ EQ@37..38 "="
+ WHITESPACE@38..39 " "
+ CURLY_GROUP@39..58
+ L_CURLY@39..40 "{"
+ WORD@40..49 "Angenendt"
+ COMMA@49..50 ","
+ WHITESPACE@50..51 " "
+ WORD@51..57 "Arnold"
+ R_CURLY@57..58 "}"
+ COMMA@58..59 ","
+ WHITESPACE@59..64 "\n "
+ FIELD@64..166
+ NAME@64..69 "title"
+ WHITESPACE@69..77 " "
+ EQ@77..78 "="
+ WHITESPACE@78..79 " "
+ CURLY_GROUP@79..165
+ L_CURLY@79..80 "{"
+ WORD@80..82 "In"
+ WHITESPACE@82..83 " "
+ WORD@83..89 "Honore"
+ WHITESPACE@89..90 " "
+ WORD@90..100 "Salvatoris"
+ NBSP@100..101 "~"
+ WORD@101..103 "--"
+ WHITESPACE@103..104 " "
+ WORD@104..107 "Vom"
+ WHITESPACE@107..108 " "
+ WORD@108..112 "Sinn"
+ WHITESPACE@112..113 " "
+ WORD@113..116 "und"
+ WHITESPACE@116..117 " "
+ WORD@117..123 "Unsinn"
+ WHITESPACE@123..124 " "
+ WORD@124..127 "der"
+ WHITESPACE@127..148 "\n "
+ WORD@148..164 "Patrozinienkunde"
+ R_CURLY@164..165 "}"
+ COMMA@165..166 ","
+ WHITESPACE@166..171 "\n "
+ FIELD@171..224
+ NAME@171..183 "journaltitle"
+ WHITESPACE@183..184 " "
+ EQ@184..185 "="
+ WHITESPACE@185..186 " "
+ CURLY_GROUP@186..223
+ L_CURLY@186..187 "{"
+ WORD@187..192 "Revue"
+ WHITESPACE@192..193 " "
+ WORD@193..203 "d'Histoire"
+ WHITESPACE@203..204 " "
+ WORD@204..208 "Eccl"
+ CURLY_GROUP@208..213
+ L_CURLY@208..209 "{"
+ ACCENT@209..212
+ ACCENT_NAME@209..211 "\\'"
+ WORD@211..212 "e"
+ R_CURLY@212..213 "}"
+ WORD@213..222 "siastique"
+ R_CURLY@222..223 "}"
+ COMMA@223..224 ","
+ WHITESPACE@224..229 "\n "
+ FIELD@229..249
+ NAME@229..233 "date"
+ WHITESPACE@233..242 " "
+ EQ@242..243 "="
+ WHITESPACE@243..244 " "
+ LITERAL@244..248
+ INTEGER@244..248 "2002"
+ COMMA@248..249 ","
+ WHITESPACE@249..254 "\n "
+ FIELD@254..272
+ NAME@254..260 "volume"
+ WHITESPACE@260..267 " "
+ EQ@267..268 "="
+ WHITESPACE@268..269 " "
+ LITERAL@269..271
+ INTEGER@269..271 "97"
+ COMMA@271..272 ","
+ WHITESPACE@272..277 "\n "
+ FIELD@277..313
+ NAME@277..282 "pages"
+ WHITESPACE@282..290 " "
+ EQ@290..291 "="
+ WHITESPACE@291..292 " "
+ CURLY_GROUP@292..312
+ L_CURLY@292..293 "{"
+ WORD@293..301 "431--456"
+ COMMA@301..302 ","
+ WHITESPACE@302..303 " "
+ WORD@303..311 "791--823"
+ R_CURLY@311..312 "}"
+ COMMA@312..313 ","
+ WHITESPACE@313..318 "\n "
+ FIELD@318..342
+ NAME@318..324 "langid"
+ WHITESPACE@324..331 " "
+ EQ@331..332 "="
+ WHITESPACE@332..333 " "
+ CURLY_GROUP@333..341
+ L_CURLY@333..334 "{"
+ WORD@334..340 "german"
+ R_CURLY@340..341 "}"
+ COMMA@341..342 ","
+ WHITESPACE@342..347 "\n "
+ FIELD@347..385
+ NAME@347..357 "indextitle"
+ WHITESPACE@357..360 " "
+ EQ@360..361 "="
+ WHITESPACE@361..362 " "
+ CURLY_GROUP@362..384
+ L_CURLY@362..363 "{"
+ WORD@363..365 "In"
+ WHITESPACE@365..366 " "
+ WORD@366..372 "Honore"
+ WHITESPACE@372..373 " "
+ WORD@373..383 "Salvatoris"
+ R_CURLY@383..384 "}"
+ COMMA@384..385 ","
+ WHITESPACE@385..390 "\n "
+ FIELD@390..428
+ NAME@390..400 "shorttitle"
+ WHITESPACE@400..403 " "
+ EQ@403..404 "="
+ WHITESPACE@404..405 " "
+ CURLY_GROUP@405..427
+ L_CURLY@405..406 "{"
+ WORD@406..408 "In"
+ WHITESPACE@408..409 " "
+ WORD@409..415 "Honore"
+ WHITESPACE@415..416 " "
+ WORD@416..426 "Salvatoris"
+ R_CURLY@426..427 "}"
+ COMMA@427..428 ","
+ WHITESPACE@428..433 "\n "
+ FIELD@433..614
+ NAME@433..443 "annotation"
+ WHITESPACE@443..446 " "
+ EQ@446..447 "="
+ WHITESPACE@447..448 " "
+ CURLY_GROUP@448..613
+ L_CURLY@448..449 "{"
+ WORD@449..450 "A"
+ WHITESPACE@450..451 " "
+ WORD@451..457 "German"
+ WHITESPACE@457..458 " "
+ WORD@458..465 "article"
+ WHITESPACE@465..466 " "
+ WORD@466..468 "in"
+ WHITESPACE@468..469 " "
+ WORD@469..470 "a"
+ WHITESPACE@470..471 " "
+ WORD@471..477 "French"
+ WHITESPACE@477..478 " "
+ WORD@478..486 "journal."
+ WHITESPACE@486..487 " "
+ WORD@487..492 "Apart"
+ WHITESPACE@492..493 " "
+ WORD@493..497 "from"
+ WHITESPACE@497..498 " "
+ WORD@498..502 "that"
+ COMMA@502..503 ","
+ WHITESPACE@503..504 " "
+ WORD@504..505 "a"
+ WHITESPACE@505..526 "\n "
+ WORD@526..533 "typical"
+ WHITESPACE@533..534 " "
+ COMMAND@534..541
+ COMMAND_NAME@534..541 "\\texttt"
+ CURLY_GROUP@541..550
+ L_CURLY@541..542 "{"
+ WORD@542..549 "article"
+ R_CURLY@549..550 "}"
+ WHITESPACE@550..551 " "
+ WORD@551..557 "entry."
+ WHITESPACE@557..558 " "
+ WORD@558..562 "Note"
+ WHITESPACE@562..563 " "
+ WORD@563..566 "the"
+ WHITESPACE@566..567 " "
+ COMMAND@567..574
+ COMMAND_NAME@567..574 "\\texttt"
+ CURLY_GROUP@574..586
+ L_CURLY@574..575 "{"
+ WORD@575..585 "indextitle"
+ R_CURLY@585..586 "}"
+ WHITESPACE@586..607 "\n "
+ WORD@607..612 "field"
+ R_CURLY@612..613 "}"
+ COMMA@613..614 ","
+ WHITESPACE@614..615 "\n"
+ R_DELIM@615..616 "}"
+
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__comment.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__comment.snap
new file mode 100644
index 0000000000..3267517c49
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__comment.snap
@@ -0,0 +1,9 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 16
+expression: "setup(r#\"@comment{aksin,\n author = {Aks{\\i}n, {\\\"O}zge and T{\\\"u}rkmen, Hayati and Artok, Levent\n and {\\c{C}}etinkaya, Bekir and Ni, Chaoying and\n B{\\\"u}y{\\\"u}kg{\\\"u}ng{\\\"o}r, Orhan and {\\\"O}zkal, Erhan},\n title = {Effect of immobilization on catalytic characteristics of\n saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}\n reactions},\n journaltitle = jomch,\n date = 2006,\n volume = 691,\n number = 13,\n pages = {3027-3036},\n indextitle = {Effect of immobilization on catalytic characteristics},\n}\"#)"
+---
+ROOT@0..637
+ TYPE@0..8 "@comment"
+ JUNK@8..637 "{aksin,\n author ..."
+
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__entry_colon.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__entry_colon.snap
new file mode 100644
index 0000000000..bff11a8d5a
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__entry_colon.snap
@@ -0,0 +1,51 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 36
+expression: "setup(r#\"@article{foo:2019,\n author = {Foo Bar},\n title = {Baz Qux},\n year = {2019},\n}\"#)"
+---
+ROOT@0..86
+ ENTRY@0..86
+ TYPE@0..8 "@article"
+ L_DELIM@8..9 "{"
+ NAME@9..17 "foo:2019"
+ COMMA@17..18 ","
+ WHITESPACE@18..23 "\n "
+ FIELD@23..42
+ NAME@23..29 "author"
+ WHITESPACE@29..30 " "
+ EQ@30..31 "="
+ WHITESPACE@31..32 " "
+ CURLY_GROUP@32..41
+ L_CURLY@32..33 "{"
+ WORD@33..36 "Foo"
+ WHITESPACE@36..37 " "
+ WORD@37..40 "Bar"
+ R_CURLY@40..41 "}"
+ COMMA@41..42 ","
+ WHITESPACE@42..47 "\n "
+ FIELD@47..65
+ NAME@47..52 "title"
+ WHITESPACE@52..53 " "
+ EQ@53..54 "="
+ WHITESPACE@54..55 " "
+ CURLY_GROUP@55..64
+ L_CURLY@55..56 "{"
+ WORD@56..59 "Baz"
+ WHITESPACE@59..60 " "
+ WORD@60..63 "Qux"
+ R_CURLY@63..64 "}"
+ COMMA@64..65 ","
+ WHITESPACE@65..70 "\n "
+ FIELD@70..84
+ NAME@70..74 "year"
+ WHITESPACE@74..75 " "
+ EQ@75..76 "="
+ WHITESPACE@76..77 " "
+ CURLY_GROUP@77..83
+ L_CURLY@77..78 "{"
+ INTEGER@78..82 "2019"
+ R_CURLY@82..83 "}"
+ COMMA@83..84 ","
+ WHITESPACE@84..85 "\n"
+ R_DELIM@85..86 "}"
+
diff --git a/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__preamble.snap b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__preamble.snap
new file mode 100644
index 0000000000..596a9d108a
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/snapshots/texlab__syntax__bibtex__tests__preamble.snap
@@ -0,0 +1,17 @@
+---
+source: src/syntax/bibtex/tests.rs
+assertion_line: 11
+expression: "setup(r#\"@preamble{\"Hello World!\"}\"#)"
+---
+ROOT@0..25
+ PREAMBLE@0..25
+ TYPE@0..9 "@preamble"
+ L_DELIM@9..10 "{"
+ QUOTE_GROUP@10..24
+ QUOTE@10..11 "\""
+ WORD@11..16 "Hello"
+ WHITESPACE@16..17 " "
+ WORD@17..23 "World!"
+ QUOTE@23..24 "\""
+ R_DELIM@24..25 "}"
+
diff --git a/support/texlab/src/syntax/bibtex/tests.rs b/support/texlab/src/syntax/bibtex/tests.rs
new file mode 100644
index 0000000000..8dce0e2df9
--- /dev/null
+++ b/support/texlab/src/syntax/bibtex/tests.rs
@@ -0,0 +1,126 @@
+use insta::assert_debug_snapshot;
+
+use super::{parse, SyntaxNode};
+
+fn setup(input: &str) -> SyntaxNode {
+ SyntaxNode::new_root(parse(input))
+}
+
+#[test]
+fn test_preamble() {
+ assert_debug_snapshot!(setup(r#"@preamble{"Hello World!"}"#));
+}
+
+#[test]
+fn test_comment() {
+ assert_debug_snapshot!(setup(
+ r#"@comment{aksin,
+ author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent
+ and {\c{C}}etinkaya, Bekir and Ni, Chaoying and
+ B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan},
+ title = {Effect of immobilization on catalytic characteristics of
+ saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}
+ reactions},
+ journaltitle = jomch,
+ date = 2006,
+ volume = 691,
+ number = 13,
+ pages = {3027-3036},
+ indextitle = {Effect of immobilization on catalytic characteristics},
+}"#
+ ));
+}
+
+#[test]
+fn test_entry_colon() {
+ assert_debug_snapshot!(setup(
+ r#"@article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+}"#
+ ));
+}
+
+#[test]
+fn test_biblatex_examples_001() {
+ assert_debug_snapshot!(setup(
+ r#"@incollection{westfahl:space,
+ author = {Westfahl, Gary},
+ title = {The True Frontier},
+ subtitle = {Confronting and Avoiding the Realities of Space in {American}
+ Science Fiction Films},
+ pages = {55-65},
+ crossref = {westfahl:frontier},
+ langid = {english},
+ langidopts = {variant=american},
+ indextitle = {True Frontier, The},
+ annotation = {A cross-referenced article from a \texttt{collection}. This is
+ an \texttt{incollection} entry with a \texttt{crossref}
+ field. Note the \texttt{subtitle} and \texttt{indextitle}
+ fields},
+}
+
+% booktitle and booksubtitle are only needed for BibTeX's less sophisticated
+% inheritance set-up to make sure westfahl:space shows correctly.
+% With Biber they are not needed.
+@collection{westfahl:frontier,
+ editor = {Westfahl, Gary},
+ title = {Space and Beyond},
+ date = 2000,
+ subtitle = {The Frontier Theme in Science Fiction},
+ publisher = {Greenwood},
+ location = {Westport, Conn. and London},
+ langid = {english},
+ langidopts = {variant=american},
+ booktitle = {Space and Beyond},
+ booksubtitle = {The Frontier Theme in Science Fiction},
+ annotation = {This is a \texttt{collection} entry. Note the format of the
+ \texttt{location} field as well as the \texttt{subtitle}
+ field},
+}"#
+ ));
+}
+
+#[test]
+fn test_biblatex_examples_002() {
+ assert_debug_snapshot!(setup(
+ r#"@string{jomch = {J.~Organomet. Chem.}}
+
+@article{aksin,
+ author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent
+ and {\c{C}}etinkaya, Bekir and Ni, Chaoying and
+ B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan},
+ title = {Effect of immobilization on catalytic characteristics of
+ saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}
+ reactions},
+ journaltitle = jomch,
+ date = 2006,
+ volume = 691,
+ number = 13,
+ pages = {3027-3036},
+ indextitle = {Effect of immobilization on catalytic characteristics},
+}"#
+ ));
+}
+
+#[test]
+fn test_biblatex_examples_003() {
+ assert_debug_snapshot!(setup(
+ r#"@article{angenendt,
+ author = {Angenendt, Arnold},
+ title = {In Honore Salvatoris~-- Vom Sinn und Unsinn der
+ Patrozinienkunde},
+ journaltitle = {Revue d'Histoire Eccl{\'e}siastique},
+ date = 2002,
+ volume = 97,
+ pages = {431--456, 791--823},
+ langid = {german},
+ indextitle = {In Honore Salvatoris},
+ shorttitle = {In Honore Salvatoris},
+ annotation = {A German article in a French journal. Apart from that, a
+ typical \texttt{article} entry. Note the \texttt{indextitle}
+ field},
+}"#
+ ));
+}
diff --git a/support/texlab/src/syntax/build_log.rs b/support/texlab/src/syntax/build_log.rs
index f91ce974d0..a717bdb877 100644
--- a/support/texlab/src/syntax/build_log.rs
+++ b/support/texlab/src/syntax/build_log.rs
@@ -65,7 +65,7 @@ fn extract_matches(
level: BuildErrorLevel,
) -> Vec<BuildError> {
let mut errors = Vec::new();
- for result in regex.find_iter(&log) {
+ for result in regex.find_iter(log) {
let captures = regex.captures(&log[result.start()..result.end()]).unwrap();
let message = captures
.name("msg")
@@ -81,7 +81,7 @@ fn extract_matches(
if let Some(range) = ranges.iter().find(|range| range.contains(result.start())) {
let line = captures
.name("line")
- .map(|result| u32::from_str_radix(result.as_str(), 10).unwrap() - 1);
+ .map(|result| result.as_str().parse::<u32>().unwrap() - 1);
errors.push(BuildError {
relative_path: range.path.clone(),
@@ -98,8 +98,8 @@ fn prepare_log(log: &str) -> String {
let mut old_lines = log.lines();
let mut new_lines: Vec<String> = Vec::new();
while let Some(line) = old_lines.next() {
- if PACKAGE_MESSAGE_REGEX.is_match(&line) {
- let captures = PACKAGE_MESSAGE_REGEX.captures(&line).unwrap();
+ if PACKAGE_MESSAGE_REGEX.is_match(line) {
+ let captures = PACKAGE_MESSAGE_REGEX.captures(line).unwrap();
if let Some(last_line) = new_lines.last_mut() {
last_line.push(' ');
last_line.push_str(captures.name("msg").unwrap().as_str());
@@ -107,7 +107,7 @@ fn prepare_log(log: &str) -> String {
} else if line.ends_with("...") {
let mut new_line = line[line.len() - 3..].to_owned();
if let Some(old_line) = old_lines.next() {
- new_line.push_str(&old_line);
+ new_line.push_str(old_line);
}
new_lines.push(new_line);
} else if line.chars().count() == MAX_LINE_LENGTH {
@@ -275,7 +275,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![
BuildError {
relative_path: "./parent.tex".into(),
@@ -365,7 +365,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![BuildError {
relative_path: "./child.tex".into(),
level: BuildErrorLevel::Error,
@@ -447,7 +447,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![
BuildError {
relative_path: "./parent.tex".into(),
@@ -555,7 +555,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![
BuildError {
relative_path: "/TexLive/texmf-dist/tex/generic/babel/babel.sty".into(),
@@ -965,7 +965,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![
BuildError {
relative_path: "./parent.tex".into(),
@@ -1118,7 +1118,7 @@ mod tests {
};
assert_eq!(
- parse(&log).errors,
+ parse(log).errors,
vec![
BuildError {
relative_path: "./parent.tex".into(),
@@ -1342,6 +1342,6 @@ mod tests {
1 words of extra memory for PDF output out of 10000 (max. 10000000)"#,
};
- assert_debug_snapshot!(parse(&log).errors);
+ assert_debug_snapshot!(parse(log).errors);
}
}
diff --git a/support/texlab/src/syntax/latex.rs b/support/texlab/src/syntax/latex.rs
index 7839c1795b..2d8918e49e 100644
--- a/support/texlab/src/syntax/latex.rs
+++ b/support/texlab/src/syntax/latex.rs
@@ -12,25 +12,23 @@ pub use self::{
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Language {}
+pub enum LatexLanguage {}
-impl cstree::Language for Language {
+impl rowan::Language for LatexLanguage {
type Kind = SyntaxKind;
- fn kind_from_raw(raw: cstree::SyntaxKind) -> Self::Kind {
+ fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
assert!(raw.0 <= ROOT as u16);
unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
}
- fn kind_to_raw(kind: Self::Kind) -> cstree::SyntaxKind {
+ fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
-pub type SyntaxNode = cstree::ResolvedNode<Language>;
+pub type SyntaxNode = rowan::SyntaxNode<LatexLanguage>;
-pub type SyntaxToken = cstree::ResolvedToken<Language>;
+pub type SyntaxToken = rowan::SyntaxToken<LatexLanguage>;
-pub type SyntaxElement = cstree::ResolvedElement<Language>;
-
-pub type SyntaxElementRef<'a> = cstree::ResolvedElementRef<'a, Language>;
+pub type SyntaxElement = rowan::SyntaxElement<LatexLanguage>;
diff --git a/support/texlab/src/syntax/latex/analysis.rs b/support/texlab/src/syntax/latex/analysis.rs
index 2849ade1be..02d9b315ba 100644
--- a/support/texlab/src/syntax/latex/analysis.rs
+++ b/support/texlab/src/syntax/latex/analysis.rs
@@ -2,6 +2,7 @@ mod command;
mod distro_file;
mod environment;
mod explicit_link;
+mod graphics_path;
mod implicit_link;
mod label_name;
mod label_number;
@@ -15,6 +16,7 @@ use self::{
command::{analyze_command, analyze_command_definition},
environment::analyze_begin,
explicit_link::{analyze_import, analyze_include},
+ graphics_path::analyze_graphics_path,
implicit_link::analyze_implicit_links,
label_name::analyze_label_name,
label_number::analyze_label_number,
@@ -24,14 +26,15 @@ use self::{
pub fn analyze(context: &mut LatexAnalyzerContext, root: &latex::SyntaxNode) {
analyze_implicit_links(context);
for node in root.descendants() {
- analyze_command(context, node)
- .or_else(|| analyze_command_definition(context, node))
- .or_else(|| analyze_begin(context, node))
- .or_else(|| analyze_include(context, node))
- .or_else(|| analyze_import(context, node))
- .or_else(|| analyze_label_name(context, node))
- .or_else(|| analyze_label_number(context, node))
- .or_else(|| analyze_theorem_definition(context, node));
+ analyze_command(context, node.clone())
+ .or_else(|| analyze_command_definition(context, node.clone()))
+ .or_else(|| analyze_begin(context, node.clone()))
+ .or_else(|| analyze_include(context, node.clone()))
+ .or_else(|| analyze_import(context, node.clone()))
+ .or_else(|| analyze_label_name(context, node.clone()))
+ .or_else(|| analyze_label_number(context, node.clone()))
+ .or_else(|| analyze_theorem_definition(context, node.clone()))
+ .or_else(|| analyze_graphics_path(context, node));
}
context.extras.has_document_environment = context.extras.environment_names.contains("document");
}
diff --git a/support/texlab/src/syntax/latex/analysis/command.rs b/support/texlab/src/syntax/latex/analysis/command.rs
index b8b3f0ef9a..60aceef725 100644
--- a/support/texlab/src/syntax/latex/analysis/command.rs
+++ b/support/texlab/src/syntax/latex/analysis/command.rs
@@ -1,8 +1,10 @@
-use crate::syntax::{latex, CstNode};
+use rowan::ast::AstNode;
+
+use crate::syntax::latex;
use super::LatexAnalyzerContext;
-pub fn analyze_command(context: &mut LatexAnalyzerContext, node: &latex::SyntaxNode) -> Option<()> {
+pub fn analyze_command(context: &mut LatexAnalyzerContext, node: latex::SyntaxNode) -> Option<()> {
let command = latex::GenericCommand::cast(node)?;
context
.extras
@@ -13,7 +15,7 @@ pub fn analyze_command(context: &mut LatexAnalyzerContext, node: &latex::SyntaxN
pub fn analyze_command_definition(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let definition = latex::CommandDefinition::cast(node)?;
context
diff --git a/support/texlab/src/syntax/latex/analysis/distro_file.rs b/support/texlab/src/syntax/latex/analysis/distro_file.rs
index 4c747ce760..f278c33627 100644
--- a/support/texlab/src/syntax/latex/analysis/distro_file.rs
+++ b/support/texlab/src/syntax/latex/analysis/distro_file.rs
@@ -1,6 +1,8 @@
-use crate::{distro::Resolver, Uri};
+use lsp_types::Url;
-pub fn resolve_distro_file(resolver: &Resolver, stem: &str, extensions: &[&str]) -> Option<Uri> {
+use crate::distro::Resolver;
+
+pub fn resolve_distro_file(resolver: &Resolver, stem: &str, extensions: &[&str]) -> Option<Url> {
let mut document = resolver.files_by_name.get(stem);
for extension in extensions {
document = document.or_else(|| {
@@ -8,7 +10,7 @@ pub fn resolve_distro_file(resolver: &Resolver, stem: &str, extensions: &[&str])
resolver.files_by_name.get(full_name.as_str())
});
}
- document.and_then(|path| Uri::from_file_path(path).ok())
+ document.and_then(|path| Url::from_file_path(path).ok())
}
#[cfg(test)]
@@ -31,12 +33,12 @@ mod tests {
assert_eq!(
resolve_distro_file(&resolver, "foo", &["tex"]),
- Some(Uri::from_file_path("C:/distro/foo.tex").unwrap())
+ Some(Url::from_file_path("C:/distro/foo.tex").unwrap())
);
assert_eq!(
resolve_distro_file(&resolver, "foo", &["sty"]),
- Some(Uri::from_file_path("C:/distro/foo.sty").unwrap())
+ Some(Url::from_file_path("C:/distro/foo.sty").unwrap())
);
assert_eq!(resolve_distro_file(&resolver, "foo", &["cls"]), None);
@@ -58,12 +60,12 @@ mod tests {
assert_eq!(
resolve_distro_file(&resolver, "foo", &["tex"]),
- Some(Uri::from_file_path("/distro/foo.tex").unwrap())
+ Some(Url::from_file_path("/distro/foo.tex").unwrap())
);
assert_eq!(
resolve_distro_file(&resolver, "foo", &["sty"]),
- Some(Uri::from_file_path("/distro/foo.sty").unwrap())
+ Some(Url::from_file_path("/distro/foo.sty").unwrap())
);
assert_eq!(resolve_distro_file(&resolver, "foo", &["cls"]), None);
diff --git a/support/texlab/src/syntax/latex/analysis/environment.rs b/support/texlab/src/syntax/latex/analysis/environment.rs
index 7cda0c126b..e99483f706 100644
--- a/support/texlab/src/syntax/latex/analysis/environment.rs
+++ b/support/texlab/src/syntax/latex/analysis/environment.rs
@@ -1,11 +1,13 @@
-use crate::syntax::{latex, CstNode};
+use rowan::ast::AstNode;
+
+use crate::syntax::latex;
use super::LatexAnalyzerContext;
-pub fn analyze_begin(context: &mut LatexAnalyzerContext, node: &latex::SyntaxNode) -> Option<()> {
+pub fn analyze_begin(context: &mut LatexAnalyzerContext, node: latex::SyntaxNode) -> Option<()> {
let begin = latex::Begin::cast(node)?;
let name = begin.name()?.key()?.to_string();
let extras = &mut context.extras;
- extras.environment_names.insert(name.into());
+ extras.environment_names.insert(name);
Some(())
}
diff --git a/support/texlab/src/syntax/latex/analysis/explicit_link.rs b/support/texlab/src/syntax/latex/analysis/explicit_link.rs
index 48f8d6b5ee..77e2cfb696 100644
--- a/support/texlab/src/syntax/latex/analysis/explicit_link.rs
+++ b/support/texlab/src/syntax/latex/analysis/explicit_link.rs
@@ -1,12 +1,14 @@
use std::sync::Arc;
-use crate::syntax::{latex, CstNode};
+use rowan::ast::AstNode;
+
+use crate::syntax::latex;
use super::{
distro_file::resolve_distro_file, ExplicitLink, ExplicitLinkKind, LatexAnalyzerContext,
};
-pub fn analyze_include(context: &mut LatexAnalyzerContext, node: &latex::SyntaxNode) -> Option<()> {
+pub fn analyze_include(context: &mut LatexAnalyzerContext, node: latex::SyntaxNode) -> Option<()> {
let include = latex::Include::cast(node)?;
let kind = match include.syntax().kind() {
latex::LATEX_INCLUDE => ExplicitLinkKind::Latex,
@@ -25,20 +27,20 @@ pub fn analyze_include(context: &mut LatexAnalyzerContext, node: &latex::SyntaxN
for path in include.path_list()?.keys() {
let stem = path.to_string();
- let mut targets = vec![Arc::new(context.base_uri.join(&stem).ok()?.into())];
+ let mut targets = vec![Arc::new(context.base_uri.join(&stem).ok()?)];
for extension in extensions {
let path = format!("{}.{}", stem, extension);
- targets.push(Arc::new(context.base_uri.join(&path).ok()?.into()));
+ targets.push(Arc::new(context.base_uri.join(&path).ok()?));
}
- resolve_distro_file(&context.inner.resolver.lock().unwrap(), &stem, extensions)
+ resolve_distro_file(&context.environment.resolver, &stem, extensions)
.into_iter()
.for_each(|target| targets.push(Arc::new(target)));
context.extras.explicit_links.push(ExplicitLink {
kind,
stem: stem.into(),
- stem_range: path.small_range(),
+ stem_range: latex::small_range(&path),
targets,
});
}
@@ -46,25 +48,23 @@ pub fn analyze_include(context: &mut LatexAnalyzerContext, node: &latex::SyntaxN
Some(())
}
-pub fn analyze_import(context: &mut LatexAnalyzerContext, node: &latex::SyntaxNode) -> Option<()> {
+pub fn analyze_import(context: &mut LatexAnalyzerContext, node: latex::SyntaxNode) -> Option<()> {
let import = latex::Import::cast(node)?;
let mut targets = Vec::new();
let directory = match import.directory().and_then(|dir| dir.key()) {
- Some(dir) => Arc::new(context.base_uri.join(&dir.to_string()).ok()?.into()),
+ Some(dir) => Arc::new(context.base_uri.join(&dir.to_string()).ok()?),
None => Arc::clone(&context.base_uri),
};
let file = import.file()?.key()?;
let stem = file.to_string();
- targets.push(Arc::new(directory.join(&stem).ok()?.into()));
- targets.push(Arc::new(
- directory.join(&format!("{}.tex", stem)).ok()?.into(),
- ));
+ targets.push(Arc::new(directory.join(&stem).ok()?));
+ targets.push(Arc::new(directory.join(&format!("{}.tex", stem)).ok()?));
context.extras.explicit_links.push(ExplicitLink {
stem: stem.into(),
- stem_range: file.small_range(),
+ stem_range: latex::small_range(&file),
targets,
kind: ExplicitLinkKind::Latex,
});
diff --git a/support/texlab/src/syntax/latex/analysis/graphics_path.rs b/support/texlab/src/syntax/latex/analysis/graphics_path.rs
new file mode 100644
index 0000000000..477094b841
--- /dev/null
+++ b/support/texlab/src/syntax/latex/analysis/graphics_path.rs
@@ -0,0 +1,21 @@
+use rowan::ast::AstNode;
+
+use crate::syntax::latex;
+
+use super::LatexAnalyzerContext;
+
+pub fn analyze_graphics_path(
+ context: &mut LatexAnalyzerContext,
+ node: latex::SyntaxNode,
+) -> Option<()> {
+ let definition = latex::GraphicsPath::cast(node)?;
+ for path in definition
+ .path_list()
+ .filter_map(|group| group.key())
+ .map(|path| path.to_string())
+ {
+ context.extras.graphics_paths.insert(path);
+ }
+
+ Some(())
+}
diff --git a/support/texlab/src/syntax/latex/analysis/implicit_link.rs b/support/texlab/src/syntax/latex/analysis/implicit_link.rs
index 74055b3e31..bba3acfb80 100644
--- a/support/texlab/src/syntax/latex/analysis/implicit_link.rs
+++ b/support/texlab/src/syntax/latex/analysis/implicit_link.rs
@@ -1,6 +1,6 @@
use std::sync::Arc;
-use crate::Uri;
+use lsp_types::Url;
use super::LatexAnalyzerContext;
@@ -10,32 +10,41 @@ pub fn analyze_implicit_links(context: &mut LatexAnalyzerContext) {
context.extras.implicit_links.pdf = find_by_extension(context, "pdf").unwrap_or_default();
}
-fn find_by_extension(context: &LatexAnalyzerContext, extension: &str) -> Option<Vec<Arc<Uri>>> {
- let mut targets: Vec<Arc<Uri>> = Vec::new();
- targets.push(Arc::new(context.document_uri.with_extension(extension)?));
+fn find_by_extension(context: &LatexAnalyzerContext, extension: &str) -> Option<Vec<Arc<Url>>> {
+ let mut targets = vec![Arc::new(with_extension(&context.document_uri, extension)?)];
if context.document_uri.scheme() == "file" {
let file_path = context.document_uri.to_file_path().ok()?;
let file_stem = file_path.file_stem()?;
let aux_name = format!("{}.{}", file_stem.to_str()?, extension);
- let options = context.inner.options.read().unwrap();
+ let options = &context.environment.options;
if let Some(root_dir) = options.root_directory.as_ref() {
let path = context
- .inner
+ .environment
.current_directory
.join(root_dir)
.join(&aux_name);
- targets.push(Arc::new(Uri::from_file_path(path).ok()?));
+ targets.push(Arc::new(Url::from_file_path(path).ok()?));
}
if let Some(build_dir) = options.aux_directory.as_ref() {
let path = context
- .inner
+ .environment
.current_directory
.join(build_dir)
.join(&aux_name);
- targets.push(Arc::new(Uri::from_file_path(path).ok()?));
+ targets.push(Arc::new(Url::from_file_path(path).ok()?));
}
}
Some(targets)
}
+
+fn with_extension(uri: &Url, extension: &str) -> Option<Url> {
+ let file_name = uri.path_segments()?.last()?;
+ let file_stem = file_name
+ .rfind('.')
+ .map(|i| &file_name[..i])
+ .unwrap_or(file_name);
+
+ uri.join(&format!("{}.{}", file_stem, extension)).ok()
+}
diff --git a/support/texlab/src/syntax/latex/analysis/label_name.rs b/support/texlab/src/syntax/latex/analysis/label_name.rs
index e5ff6829cd..1e3339db5e 100644
--- a/support/texlab/src/syntax/latex/analysis/label_name.rs
+++ b/support/texlab/src/syntax/latex/analysis/label_name.rs
@@ -1,27 +1,28 @@
use latex::LabelReferenceRange;
+use rowan::ast::AstNode;
-use crate::syntax::{latex, CstNode};
+use crate::syntax::latex;
use super::{LabelName, LatexAnalyzerContext};
pub fn analyze_label_name(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
- analyze_label_definition_name(context, node)
- .or_else(|| analyze_label_reference_name(context, node))
+ analyze_label_definition_name(context, node.clone())
+ .or_else(|| analyze_label_reference_name(context, node.clone()))
.or_else(|| analyze_label_reference_range_name(context, node))
}
fn analyze_label_definition_name(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let label = latex::LabelDefinition::cast(node)?;
let name = label.name()?.key()?;
context.extras.label_names.push(LabelName {
text: name.to_string().into(),
- range: name.small_range(),
+ range: latex::small_range(&name),
is_definition: true,
});
Some(())
@@ -29,13 +30,13 @@ fn analyze_label_definition_name(
fn analyze_label_reference_name(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let label = latex::LabelReference::cast(node)?;
for name in label.name_list()?.keys() {
context.extras.label_names.push(LabelName {
text: name.to_string().into(),
- range: name.small_range(),
+ range: latex::small_range(&name),
is_definition: false,
});
}
@@ -44,13 +45,13 @@ fn analyze_label_reference_name(
fn analyze_label_reference_range_name(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let label = LabelReferenceRange::cast(node)?;
if let Some(name1) = label.from().and_then(|name| name.key()) {
context.extras.label_names.push(LabelName {
text: name1.to_string().into(),
- range: name1.small_range(),
+ range: latex::small_range(&name1),
is_definition: false,
});
}
@@ -58,7 +59,7 @@ fn analyze_label_reference_range_name(
if let Some(name2) = label.to().and_then(|name| name.key()) {
context.extras.label_names.push(LabelName {
text: name2.to_string().into(),
- range: name2.small_range(),
+ range: latex::small_range(&name2),
is_definition: false,
});
}
diff --git a/support/texlab/src/syntax/latex/analysis/label_number.rs b/support/texlab/src/syntax/latex/analysis/label_number.rs
index 93ad09523f..61ad348680 100644
--- a/support/texlab/src/syntax/latex/analysis/label_number.rs
+++ b/support/texlab/src/syntax/latex/analysis/label_number.rs
@@ -1,10 +1,12 @@
-use crate::syntax::{latex, CstNode};
+use rowan::ast::AstNode;
+
+use crate::syntax::latex;
use super::LatexAnalyzerContext;
pub fn analyze_label_number(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let number = latex::LabelNumber::cast(node)?;
let name = number.name()?.key()?.to_string();
diff --git a/support/texlab/src/syntax/latex/analysis/theorem.rs b/support/texlab/src/syntax/latex/analysis/theorem.rs
index 50236e24a5..70047362d1 100644
--- a/support/texlab/src/syntax/latex/analysis/theorem.rs
+++ b/support/texlab/src/syntax/latex/analysis/theorem.rs
@@ -1,13 +1,12 @@
-use crate::syntax::{
- latex::{self, HasCurly},
- CstNode,
-};
+use rowan::ast::AstNode;
+
+use crate::syntax::latex::{self, HasCurly};
use super::{LatexAnalyzerContext, TheoremEnvironment};
pub fn analyze_theorem_definition(
context: &mut LatexAnalyzerContext,
- node: &latex::SyntaxNode,
+ node: latex::SyntaxNode,
) -> Option<()> {
let theorem = latex::TheoremDefinition::cast(node)?;
let name = theorem.name()?.key()?.to_string();
diff --git a/support/texlab/src/syntax/latex/analysis/types.rs b/support/texlab/src/syntax/latex/analysis/types.rs
index bd2789b3b7..7fb938e3d2 100644
--- a/support/texlab/src/syntax/latex/analysis/types.rs
+++ b/support/texlab/src/syntax/latex/analysis/types.rs
@@ -1,16 +1,17 @@
use std::sync::Arc;
-use cstree::TextRange;
+use lsp_types::Url;
+use rowan::TextRange;
use rustc_hash::{FxHashMap, FxHashSet};
use smol_str::SmolStr;
-use crate::{ServerContext, Uri};
+use crate::Environment;
#[derive(Debug)]
-pub struct LatexAnalyzerContext {
- pub inner: Arc<ServerContext>,
- pub document_uri: Arc<Uri>,
- pub base_uri: Arc<Uri>,
+pub struct LatexAnalyzerContext<'a> {
+ pub environment: &'a Environment,
+ pub document_uri: Arc<Url>,
+ pub base_uri: Arc<Url>,
pub extras: Extras,
}
@@ -24,13 +25,14 @@ pub struct Extras {
pub label_names: Vec<LabelName>,
pub label_numbers_by_name: FxHashMap<String, String>,
pub theorem_environments: Vec<TheoremEnvironment>,
+ pub graphics_paths: FxHashSet<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Hash)]
pub struct ImplicitLinks {
- pub aux: Vec<Arc<Uri>>,
- pub log: Vec<Arc<Uri>>,
- pub pdf: Vec<Arc<Uri>>,
+ pub aux: Vec<Arc<Url>>,
+ pub log: Vec<Arc<Url>>,
+ pub pdf: Vec<Arc<Url>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
@@ -45,7 +47,7 @@ pub enum ExplicitLinkKind {
pub struct ExplicitLink {
pub stem: SmolStr,
pub stem_range: TextRange,
- pub targets: Vec<Arc<Uri>>,
+ pub targets: Vec<Arc<Url>>,
pub kind: ExplicitLinkKind,
}
diff --git a/support/texlab/src/syntax/latex/cst.rs b/support/texlab/src/syntax/latex/cst.rs
index 80c3375304..06c187213d 100644
--- a/support/texlab/src/syntax/latex/cst.rs
+++ b/support/texlab/src/syntax/latex/cst.rs
@@ -1,20 +1,43 @@
-use cstree::TextRange;
use itertools::{EitherOrBoth, Itertools};
+use rowan::{ast::AstNode, TextRange};
+
+use super::{
+ LatexLanguage,
+ SyntaxKind::{self, *},
+ SyntaxNode, SyntaxToken,
+};
+
+pub fn small_range(node: &dyn AstNode<Language = LatexLanguage>) -> TextRange {
+ let full_range = node.syntax().text_range();
+ let start = full_range.start();
+ let mut token = node.syntax().last_token();
+ while let Some(current) = token {
+ if !matches!(current.kind(), LINE_BREAK | WHITESPACE | COMMENT) {
+ return TextRange::new(start, current.text_range().end());
+ }
+ token = current.prev_token();
+ }
-use crate::syntax::CstNode;
-
-use super::{Language, SyntaxKind::*, SyntaxNode, SyntaxToken};
+ TextRange::new(start, start)
+}
macro_rules! cst_node {
($name:ident, $($kind:pat),+) => {
- #[derive(Clone, Copy)]
+ #[derive(Clone)]
#[repr(transparent)]
- pub struct $name<'a>(&'a SyntaxNode);
+ pub struct $name(SyntaxNode);
+
+ impl AstNode for $name {
+ type Language = LatexLanguage;
- impl<'a> CstNode<'a> for $name<'a> {
- type Lang = Language;
+ fn can_cast(kind: SyntaxKind) -> bool {
+ match kind {
+ $($kind => true,)+
+ _ => false,
+ }
+ }
- fn cast(node: &'a cstree::ResolvedNode<Self::Lang>) -> Option<Self>
+ fn cast(node: SyntaxNode) -> Option<Self>
where
Self: Sized,
{
@@ -24,30 +47,17 @@ macro_rules! cst_node {
}
}
- fn syntax(&self) -> &'a cstree::ResolvedNode<Self::Lang> {
+ fn syntax(&self) -> &SyntaxNode {
&self.0
}
-
- fn small_range(&self) -> TextRange {
- let full_range = self.syntax().text_range();
- let start = full_range.start();
- let mut token = self.syntax().last_token();
- while let Some(current) = token {
- if !matches!(current.kind(), LINE_BREAK | WHITESPACE | COMMENT) {
- return TextRange::new(start, current.text_range().end());
- }
- token = current.prev_token();
- }
- TextRange::new(start, start)
- }
}
};
}
cst_node!(Text, TEXT);
-impl<'a> Text<'a> {
- pub fn words(&self) -> impl Iterator<Item = &'a SyntaxToken> {
+impl Text {
+ pub fn words(&self) -> impl Iterator<Item = SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -55,15 +65,15 @@ impl<'a> Text<'a> {
}
}
-pub trait HasCurly<'a>: CstNode<'a, Lang = Language> {
- fn left_curly(&self) -> Option<&'a SyntaxToken> {
+pub trait HasCurly: AstNode<Language = LatexLanguage> {
+ fn left_curly(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
.find(|node| node.kind() == L_CURLY)
}
- fn right_curly(&self) -> Option<&'a SyntaxToken> {
+ fn right_curly(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -91,19 +101,19 @@ pub trait HasCurly<'a>: CstNode<'a, Lang = Language> {
cst_node!(CurlyGroup, CURLY_GROUP);
-impl<'a> HasCurly<'a> for CurlyGroup<'a> {}
+impl HasCurly for CurlyGroup {}
-impl<'a> CurlyGroup<'a> {}
+impl CurlyGroup {}
-pub trait HasBrack<'a>: CstNode<'a, Lang = Language> {
- fn left_brack(&self) -> Option<&'a SyntaxToken> {
+pub trait HasBrack: AstNode<Language = LatexLanguage> {
+ fn left_brack(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
.find(|node| node.kind() == L_BRACK)
}
- fn right_brack(&self) -> Option<&'a SyntaxToken> {
+ fn right_brack(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -131,19 +141,19 @@ pub trait HasBrack<'a>: CstNode<'a, Lang = Language> {
cst_node!(BrackGroup, BRACK_GROUP);
-impl<'a> BrackGroup<'a> {}
+impl BrackGroup {}
-impl<'a> HasBrack<'a> for BrackGroup<'a> {}
+impl HasBrack for BrackGroup {}
-pub trait HasParen<'a>: CstNode<'a, Lang = Language> {
- fn left_paren(&self) -> Option<&'a SyntaxToken> {
+pub trait HasParen: AstNode<Language = LatexLanguage> {
+ fn left_paren(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
.find(|node| node.kind() == L_PAREN)
}
- fn right_paren(&self) -> Option<&'a SyntaxToken> {
+ fn right_paren(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -153,62 +163,62 @@ pub trait HasParen<'a>: CstNode<'a, Lang = Language> {
cst_node!(ParenGroup, PAREN_GROUP);
-impl<'a> HasParen<'a> for ParenGroup<'a> {}
+impl HasParen for ParenGroup {}
cst_node!(MixedGroup, MIXED_GROUP);
-impl<'a> MixedGroup<'a> {
- pub fn left_delim(&self) -> Option<&'a SyntaxToken> {
+impl MixedGroup {
+ pub fn left_delim(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
- .find(|node| matches!(node.kind().into(), L_BRACK | L_PAREN))
+ .find(|node| matches!(node.kind(), L_BRACK | L_PAREN))
}
- pub fn right_delim(&self) -> Option<&'a SyntaxToken> {
+ pub fn right_delim(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
- .find(|node| matches!(node.kind().into(), R_BRACK | R_PAREN))
+ .find(|node| matches!(node.kind(), R_BRACK | R_PAREN))
}
}
cst_node!(CurlyGroupWord, CURLY_GROUP_WORD);
-impl<'a> HasCurly<'a> for CurlyGroupWord<'a> {}
+impl HasCurly for CurlyGroupWord {}
-impl<'a> CurlyGroupWord<'a> {
- pub fn key(&self) -> Option<Key<'a>> {
+impl CurlyGroupWord {
+ pub fn key(&self) -> Option<Key> {
self.syntax().children().find_map(Key::cast)
}
}
cst_node!(BrackGroupWord, BRACK_GROUP_WORD);
-impl<'a> HasBrack<'a> for BrackGroupWord<'a> {}
+impl HasBrack for BrackGroupWord {}
-impl<'a> BrackGroupWord<'a> {
- pub fn key(&self) -> Option<Key<'a>> {
+impl BrackGroupWord {
+ pub fn key(&self) -> Option<Key> {
self.syntax().children().find_map(Key::cast)
}
}
cst_node!(CurlyGroupWordList, CURLY_GROUP_WORD_LIST);
-impl<'a> HasCurly<'a> for CurlyGroupWordList<'a> {}
+impl HasCurly for CurlyGroupWordList {}
-impl<'a> CurlyGroupWordList<'a> {
- pub fn keys(&self) -> impl Iterator<Item = Key<'a>> {
+impl CurlyGroupWordList {
+ pub fn keys(&self) -> impl Iterator<Item = Key> {
self.syntax().children().filter_map(Key::cast)
}
}
cst_node!(CurlyGroupCommand, CURLY_GROUP_COMMAND);
-impl<'a> HasCurly<'a> for CurlyGroupCommand<'a> {}
+impl HasCurly for CurlyGroupCommand {}
-impl<'a> CurlyGroupCommand<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl CurlyGroupCommand {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -218,8 +228,8 @@ impl<'a> CurlyGroupCommand<'a> {
cst_node!(Key, KEY);
-impl<'a> Key<'a> {
- pub fn words(&self) -> impl Iterator<Item = &'a SyntaxToken> {
+impl Key {
+ pub fn words(&self) -> impl Iterator<Item = SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -227,7 +237,7 @@ impl<'a> Key<'a> {
}
}
-impl<'a> PartialEq for Key<'a> {
+impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
self.words()
.zip_longest(other.words())
@@ -238,11 +248,18 @@ impl<'a> PartialEq for Key<'a> {
}
}
-impl<'a> Eq for Key<'a> {}
+impl Eq for Key {}
-impl<'a> ToString for Key<'a> {
+impl ToString for Key {
fn to_string(&self) -> String {
- self.words().map(|word| word.text()).join(" ")
+ let mut buf = String::new();
+ for word in self.words() {
+ buf.push_str(word.text());
+ buf.push(' ');
+ }
+
+ buf.pop().unwrap();
+ buf
}
}
@@ -250,48 +267,48 @@ cst_node!(Value, VALUE);
cst_node!(KeyValuePair, KEY_VALUE_PAIR);
-impl<'a> KeyValuePair<'a> {
- pub fn key(&self) -> Option<Key<'a>> {
+impl KeyValuePair {
+ pub fn key(&self) -> Option<Key> {
self.syntax().children().find_map(Key::cast)
}
- pub fn value(&self) -> Option<Value<'a>> {
+ pub fn value(&self) -> Option<Value> {
self.syntax().children().find_map(Value::cast)
}
}
cst_node!(KeyValueBody, KEY_VALUE_BODY);
-impl<'a> KeyValueBody<'a> {
- pub fn pairs(&self) -> impl Iterator<Item = KeyValuePair<'a>> {
+impl KeyValueBody {
+ pub fn pairs(&self) -> impl Iterator<Item = KeyValuePair> {
self.syntax().children().filter_map(KeyValuePair::cast)
}
}
-pub trait HasKeyValueBody<'a>: CstNode<'a, Lang = Language> {
- fn body(&self) -> Option<KeyValueBody<'a>> {
+pub trait HasKeyValueBody: AstNode<Language = LatexLanguage> {
+ fn body(&self) -> Option<KeyValueBody> {
self.syntax().children().find_map(KeyValueBody::cast)
}
}
cst_node!(CurlyGroupKeyValue, CURLY_GROUP_KEY_VALUE);
-impl<'a> HasCurly<'a> for CurlyGroupKeyValue<'a> {}
+impl HasCurly for CurlyGroupKeyValue {}
-impl<'a> HasKeyValueBody<'a> for CurlyGroupKeyValue<'a> {}
+impl HasKeyValueBody for CurlyGroupKeyValue {}
cst_node!(BrackGroupKeyValue, BRACK_GROUP_KEY_VALUE);
-impl<'a> HasBrack<'a> for BrackGroupKeyValue<'a> {}
+impl HasBrack for BrackGroupKeyValue {}
-impl<'a> HasKeyValueBody<'a> for BrackGroupKeyValue<'a> {}
+impl HasKeyValueBody for BrackGroupKeyValue {}
cst_node!(Formula, FORMULA);
cst_node!(GenericCommand, GENERIC_COMMAND);
-impl<'a> GenericCommand<'a> {
- pub fn name(&self) -> Option<&'a SyntaxToken> {
+impl GenericCommand {
+ pub fn name(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|node| node.into_token())
@@ -303,40 +320,40 @@ cst_node!(Equation, EQUATION);
cst_node!(Begin, BEGIN);
-impl<'a> Begin<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Begin {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn options(&self) -> Option<BrackGroup<'a>> {
+ pub fn options(&self) -> Option<BrackGroup> {
self.syntax().children().find_map(BrackGroup::cast)
}
}
cst_node!(End, END);
-impl<'a> End<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl End {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(Environment, ENVIRONMENT);
-impl<'a> Environment<'a> {
- pub fn begin(&self) -> Option<Begin<'a>> {
+impl Environment {
+ pub fn begin(&self) -> Option<Begin> {
self.syntax().children().find_map(Begin::cast)
}
- pub fn end(&self) -> Option<End<'a>> {
+ pub fn end(&self) -> Option<End> {
self.syntax().children().find_map(End::cast)
}
}
@@ -352,60 +369,60 @@ cst_node!(
SUBPARAGRAPH
);
-impl<'a> Section<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Section {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroup<'a>> {
+ pub fn name(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(EnumItem, ENUM_ITEM);
-impl<'a> EnumItem<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl EnumItem {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn label(&self) -> Option<BrackGroup<'a>> {
+ pub fn label(&self) -> Option<BrackGroup> {
self.syntax().children().find_map(BrackGroup::cast)
}
}
cst_node!(Caption, CAPTION);
-impl<'a> Caption<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Caption {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn short(&self) -> Option<BrackGroup<'a>> {
+ pub fn short(&self) -> Option<BrackGroup> {
self.syntax().children().find_map(BrackGroup::cast)
}
- pub fn long(&self) -> Option<CurlyGroup<'a>> {
+ pub fn long(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(Citation, CITATION);
-impl<'a> Citation<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Citation {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn prenote(&self) -> Option<BrackGroup<'a>> {
+ pub fn prenote(&self) -> Option<BrackGroup> {
self.syntax().children().find_map(BrackGroup::cast)
}
- pub fn postnote(&self) -> Option<BrackGroup<'a>> {
+ pub fn postnote(&self) -> Option<BrackGroup> {
self.syntax().children().filter_map(BrackGroup::cast).nth(1)
}
- pub fn key_list(&self) -> Option<CurlyGroupWordList<'a>> {
+ pub fn key_list(&self) -> Option<CurlyGroupWordList> {
self.syntax().children().find_map(CurlyGroupWordList::cast)
}
}
@@ -423,28 +440,28 @@ cst_node!(
VERBATIM_INCLUDE
);
-impl<'a> Include<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Include {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn path_list(&self) -> Option<CurlyGroupWordList<'a>> {
+ pub fn path_list(&self) -> Option<CurlyGroupWordList> {
self.syntax().children().find_map(CurlyGroupWordList::cast)
}
}
cst_node!(Import, IMPORT);
-impl<'a> Import<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl Import {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn directory(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn directory(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn file(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn file(&self) -> Option<CurlyGroupWord> {
self.syntax()
.children()
.filter_map(CurlyGroupWord::cast)
@@ -454,40 +471,40 @@ impl<'a> Import<'a> {
cst_node!(LabelDefinition, LABEL_DEFINITION);
-impl<'a> LabelDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl LabelDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(LabelReference, LABEL_REFERENCE);
-impl<'a> LabelReference<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl LabelReference {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name_list(&self) -> Option<CurlyGroupWordList<'a>> {
+ pub fn name_list(&self) -> Option<CurlyGroupWordList> {
self.syntax().children().find_map(CurlyGroupWordList::cast)
}
}
cst_node!(LabelReferenceRange, LABEL_REFERENCE_RANGE);
-impl<'a> LabelReferenceRange<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl LabelReferenceRange {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn from(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn from(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn to(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn to(&self) -> Option<CurlyGroupWord> {
self.syntax()
.children()
.filter_map(CurlyGroupWord::cast)
@@ -497,163 +514,175 @@ impl<'a> LabelReferenceRange<'a> {
cst_node!(LabelNumber, LABEL_NUMBER);
-impl<'a> LabelNumber<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl LabelNumber {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn text(&self) -> Option<CurlyGroup<'a>> {
+ pub fn text(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(TheoremDefinition, THEOREM_DEFINITION);
-impl<'a> TheoremDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl TheoremDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn description(&self) -> Option<CurlyGroup<'a>> {
+ pub fn description(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(CommandDefinition, COMMAND_DEFINITION, MATH_OPERATOR);
-impl<'a> CommandDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl CommandDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupCommand<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupCommand> {
self.syntax().children().find_map(CurlyGroupCommand::cast)
}
- pub fn implementation(&self) -> Option<CurlyGroup<'a>> {
+ pub fn implementation(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(AcronymReference, ACRONYM_REFERENCE);
-impl<'a> AcronymReference<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl AcronymReference {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
}
cst_node!(AcronymDefinition, ACRONYM_DEFINITION);
-impl<'a> AcronymDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl AcronymDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(AcronymDeclaration, ACRONYM_DECLARATION);
-impl<'a> AcronymDeclaration<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl AcronymDeclaration {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(ColorDefinition, COLOR_DEFINITION);
-impl<'a> ColorDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl ColorDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
- pub fn model(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn model(&self) -> Option<CurlyGroupWord> {
self.syntax()
.children()
.filter_map(CurlyGroupWord::cast)
.nth(1)
}
- pub fn spec(&self) -> Option<CurlyGroup<'a>> {
+ pub fn spec(&self) -> Option<CurlyGroup> {
self.syntax().children().find_map(CurlyGroup::cast)
}
}
cst_node!(ColorSetDefinition, COLOR_SET_DEFINITION);
-impl<'a> ColorSetDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl ColorSetDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn model_list(&self) -> Option<CurlyGroupWordList<'a>> {
+ pub fn model_list(&self) -> Option<CurlyGroupWordList> {
self.syntax().children().find_map(CurlyGroupWordList::cast)
}
}
cst_node!(ColorReference, COLOR_REFERENCE);
-impl<'a> ColorReference<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl ColorReference {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(GlossaryEntryReference, GLOSSARY_ENTRY_REFERENCE);
-impl<'a> GlossaryEntryReference<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl GlossaryEntryReference {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(GlossaryEntryDefinition, GLOSSARY_ENTRY_DEFINITION);
-impl<'a> GlossaryEntryDefinition<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl GlossaryEntryDefinition {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name(&self) -> Option<CurlyGroupWord<'a>> {
+ pub fn name(&self) -> Option<CurlyGroupWord> {
self.syntax().children().find_map(CurlyGroupWord::cast)
}
}
cst_node!(TikzLibraryImport, TIKZ_LIBRARY_IMPORT);
-impl<'a> TikzLibraryImport<'a> {
- pub fn command(&self) -> Option<&'a SyntaxToken> {
+impl TikzLibraryImport {
+ pub fn command(&self) -> Option<SyntaxToken> {
self.syntax().first_token()
}
- pub fn name_list(&self) -> Option<CurlyGroupWordList<'a>> {
+ pub fn name_list(&self) -> Option<CurlyGroupWordList> {
self.syntax().children().find_map(CurlyGroupWordList::cast)
}
}
+
+cst_node!(GraphicsPath, GRAPHICS_PATH);
+
+impl GraphicsPath {
+ pub fn command(&self) -> Option<SyntaxToken> {
+ self.syntax().first_token()
+ }
+
+ pub fn path_list(&self) -> impl Iterator<Item = CurlyGroupWord> {
+ self.syntax().children().filter_map(CurlyGroupWord::cast)
+ }
+}
diff --git a/support/texlab/src/syntax/latex/kind.rs b/support/texlab/src/syntax/latex/kind.rs
index 410cd4fef7..81e0ea3588 100644
--- a/support/texlab/src/syntax/latex/kind.rs
+++ b/support/texlab/src/syntax/latex/kind.rs
@@ -8,6 +8,7 @@ pub enum SyntaxKind {
LINE_BREAK,
WHITESPACE,
COMMENT,
+ VERBATIM,
L_CURLY,
R_CURLY,
L_BRACK,
@@ -59,7 +60,10 @@ pub enum SyntaxKind {
COLOR_DEFINITION_NAME,
COLOR_SET_DEFINITION_NAME,
TIKZ_LIBRARY_IMPORT_NAME,
- ENVIRONMENT_DEFINIITION_NAME,
+ ENVIRONMENT_DEFINITION_NAME,
+ BEGIN_BLOCK_COMMENT_NAME,
+ END_BLOCK_COMMENT_NAME,
+ GRAPHICS_PATH_NAME,
PREAMBLE,
TEXT,
@@ -120,6 +124,8 @@ pub enum SyntaxKind {
COLOR_SET_DEFINITION,
TIKZ_LIBRARY_IMPORT,
ENVIRONMENT_DEFINITION,
+ GRAPHICS_PATH,
+ BLOCK_COMMENT,
ROOT,
}
@@ -173,7 +179,7 @@ impl SyntaxKind {
}
}
-impl From<SyntaxKind> for cstree::SyntaxKind {
+impl From<SyntaxKind> for rowan::SyntaxKind {
fn from(kind: SyntaxKind) -> Self {
Self(kind as u16)
}
diff --git a/support/texlab/src/syntax/latex/lexer.rs b/support/texlab/src/syntax/latex/lexer.rs
index 4ea11bf377..39196375b1 100644
--- a/support/texlab/src/syntax/latex/lexer.rs
+++ b/support/texlab/src/syntax/latex/lexer.rs
@@ -5,172 +5,413 @@ use super::kind::SyntaxKind;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Logos)]
#[allow(non_camel_case_types)]
#[repr(u16)]
-enum Token {
+enum RootToken {
#[regex(r"[\r\n]+", priority = 2)]
- LINE_BREAK = 2,
+ LineBreak = 2,
#[regex(r"\s+", priority = 1)]
- WHITESPACE,
+ Whitespace,
#[regex(r"%[^\r\n]*")]
- COMMENT,
+ LineComment,
#[token("{")]
- L_CURLY,
+ LCurly,
#[token("}")]
- R_CURLY,
+ RCurly,
#[token("[")]
- L_BRACK,
+ LBrack,
#[token("]")]
- R_BRACK,
+ RBrack,
#[token("(")]
- L_PAREN,
+ LParen,
#[token(")")]
- R_PAREN,
+ RParen,
#[token(",")]
- COMMA,
+ Comma,
#[token("=")]
- EQUALITY_SIGN,
+ Eq,
#[regex(r"[^\s\\%\{\},\$\[\]\(\)=]+")]
#[error]
- WORD,
+ Word,
#[regex(r"\$\$?")]
- DOLLAR,
+ Dollar,
#[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
- GENERIC_COMMAND_NAME,
+ CommandName,
- #[regex(r"\\begin")]
- BEGIN_ENVIRONMENT_NAME,
+ #[token("\\iffalse")]
+ BeginBlockComment,
- #[regex(r"\\end")]
- END_ENVIRONMENT_NAME,
-
- #[regex(r"\\\[")]
- BEGIN_EQUATION_NAME,
-
- #[regex(r"\\\]")]
- END_EQUATION_NAME,
-
- #[regex(r"\\part\*?")]
- PART_NAME,
-
- #[regex(r"\\chapter\*?")]
- CHAPTER_NAME,
-
- #[regex(r"\\section\*?")]
- SECTION_NAME,
-
- #[regex(r"\\subsection\*?")]
- SUBSECTION_NAME,
-
- #[regex(r"\\subsubsection\*?")]
- SUBSUBSECTION_NAME,
-
- #[regex(r"\\paragraph\*?")]
- PARAGRAPH_NAME,
-
- #[regex(r"\\subparagraph\*?")]
- SUBPARAGRAPH_NAME,
-
- #[regex(r"\\item")]
- ENUM_ITEM_NAME,
-
- #[regex(r"\\caption")]
- CAPTION_NAME,
-
- #[regex(r"\\cite|\\cite\*|\\Cite|\\nocite|\\citet|\\citep|\\citet\*|\\citep\*|\\citeauthor|\\citeauthor\*|\\Citeauthor|\\Citeauthor\*|\\citetitle|\\citetitle\*|\\citeyear|\\citeyear\*|\\citedate|\\citedate\*|\\citeurl|\\fullcite|\\citeyearpar|\\citealt|\\citealp|\\citetext|\\parencite|\\parencite\*|\\Parencite|\\footcite|\\footfullcite|\\footcitetext|\\textcite|\\Textcite|\\smartcite|\\Smartcite|\\supercite|\\autocite|\\Autocite|\\autocite\*|\\Autocite\*|\\volcite|\\Volcite|\\pvolcite|\\Pvolcite|\\fvolcite|\\ftvolcite|\\svolcite|\\Svolcite|\\tvolcite|\\Tvolcite|\\avolcite|\\Avolcite|\\notecite|\\notecite|\\pnotecite|\\Pnotecite|\\fnotecite|\\citeA|\\citeA\*")]
- CITATION_NAME,
-
- #[regex(r"\\usepackage|\\RequirePackage")]
- PACKAGE_INCLUDE_NAME,
-
- #[regex(r"\\documentclass")]
- CLASS_INCLUDE_NAME,
-
- #[regex(r"\\include|\\subfileinclude|\\input|\\subfile")]
- LATEX_INCLUDE_NAME,
-
- #[regex(r"\\addbibresource")]
- BIBLATEX_INCLUDE_NAME,
-
- #[regex(r"\\bibliography")]
- BIBTEX_INCLUDE_NAME,
-
- #[regex(r"\\includegraphics")]
- GRAPHICS_INCLUDE_NAME,
-
- #[regex(r"\\includesvg")]
- SVG_INCLUDE_NAME,
-
- #[regex(r"\\includeinkscape")]
- INKSCAPE_INCLUDE_NAME,
-
- #[regex(r"\\verbatiminput|\\VerbatimInput")]
- VERBATIM_INCLUDE_NAME,
-
- #[regex(r"\\import|\\subimport|\\inputfrom|\\subimportfrom|\\includefrom|\\subincludefrom")]
- IMPORT_NAME,
-
- #[regex(r"\\label")]
- LABEL_DEFINITION_NAME,
-
- #[regex(r"\\ref|\\vref|\\Vref|\\autoref|\\pageref|\\cref|\\Cref|\\cref*|\\Cref*|\\namecref|\\nameCref|\\lcnamecref|\\namecrefs|\\nameCrefs|\\lcnamecrefs|\\labelcref|\\labelcpageref|\\eqref")]
- LABEL_REFERENCE_NAME,
-
- #[regex(r"\\crefrange\*?|\\Crefrange\*?")]
- LABEL_REFERENCE_RANGE_NAME,
-
- #[regex(r"\\newlabel")]
- LABEL_NUMBER_NAME,
-
- #[regex(r"\\newcommand\*?|\\renewcommand|\\DeclareRobustCommand")]
- COMMAND_DEFINITION_NAME,
-
- #[regex(r"\\DeclareMathOperator\*?")]
- MATH_OPERATOR_NAME,
-
- #[regex(r"\\newglossaryentry")]
- GLOSSARY_ENTRY_DEFINITION_NAME,
-
- #[regex(r"\\gls|\\Gls|\\GLS|\\glspl|\\Glspl|\\GLSpl|\\glsdisp|\\glslink|\\glstext|\\Glstext|\\GLStext|\\glsfirst|\\Glsfirst|\\GLSfirst|\\glsplural|\\Glsplural|\\GLSplural|\\glsfirstplural|\\Glsfirstplural|\\GLSfirstplural|\\glsname|\\Glsname|\\GLSname|\\glssymbol|\\Glssymbol|\\glsdesc|\\Glsdesc|\\GLSdesc|\\glsuseri|\\Glsuseri|\\GLSuseri|\\glsuserii|\\Glsuserii|\\GLSuserii|\\glsuseriii|\\Glsuseriii|\\GLSuseriii|\\glsuseriv|\\Glsuseriv|\\GLSuseriv|\\glsuserv|\\Glsuserv|\\GLSuserv|\\glsuservi|\\Glsuservi|\\GLSuservi")]
- GLOSSARY_ENTRY_REFERENCE_NAME,
-
- #[regex(r"\\newacronym")]
- ACRONYM_DEFINITION_NAME,
-
- #[regex(r"\\DeclareAcronym")]
- ACRONYM_DECLARATION_NAME,
-
- #[regex(r"\\acrshort|\\Acrshort|\\ACRshort|\\acrshortpl|\\Acrshortpl|\\ACRshortpl|\\acrlong|\\Acrlong|\\ACRlong|\\acrlongpl|\\Acrlongpl|\\ACRlongpl|\\acrfull|\\Acrfull|\\ACRfull|\\acrfullpl|\\Acrfullpl|\\ACRfullpl|\\acs|\\Acs|\\acsp|\\Acsp|\\acl|\\Acl|\\aclp|\\Aclp|\\acf|\\Acf|\\acfp|\\Acfp|\\ac|\\Ac|\\acp|\\glsentrylong|\\Glsentrylong|\\glsentrylongpl|\\Glsentrylongpl|\\glsentryshort|\\Glsentryshort|\\glsentryshortpl|\\Glsentryshortpl|\\glsentryfullpl|\\Glsentryfullpl")]
- ACRONYM_REFERENCE_NAME,
+ #[token("\\begin{asy}")]
+ #[token("\\begin{verbatim}")]
+ #[token("\\begin{lstlisting}")]
+ #[token("\\begin{minted}")]
+ #[token("\\begin{pycode}")]
+ BeginVerbatimEnvironment,
+}
- #[regex(r"\\newtheorem|\\declaretheorem")]
- THEOREM_DEFINITION_NAME,
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Logos)]
+enum CommandNameToken {
+ #[token("\\begin")]
+ BeginEnvironment,
+
+ #[token("\\end")]
+ EndEnvironment,
+
+ #[token("\\[")]
+ BeginEquation,
+
+ #[token("\\]")]
+ EndEquation,
+
+ #[token("\\part")]
+ #[token("\\part*")]
+ Part,
+
+ #[token("\\chapter")]
+ #[token("\\chapter*")]
+ Chapter,
+
+ #[token("\\section")]
+ #[token("\\section*")]
+ Section,
+
+ #[token("\\subsection")]
+ #[token("\\subsection*")]
+ Subsection,
+
+ #[token("\\subsubsection")]
+ #[token("\\subsubsection*")]
+ Subsubsection,
+
+ #[token("\\paragraph")]
+ #[token("\\paragraph*")]
+ Paragraph,
+
+ #[token("\\subparagraph")]
+ #[token("\\subparagraph*")]
+ Subparagraph,
+
+ #[token("\\item")]
+ EnumItem,
+
+ #[token("\\caption")]
+ Caption,
+
+ #[token("\\cite")]
+ #[token("\\cite*")]
+ #[token("\\Cite")]
+ #[token("\\nocite")]
+ #[token("\\citet")]
+ #[token("\\citet*")]
+ #[token("\\citep")]
+ #[token("\\citep*")]
+ #[token("\\citeauthor")]
+ #[token("\\citeauthor*")]
+ #[token("\\Citeauthor")]
+ #[token("\\Citeauthor*")]
+ #[token("\\citetitle")]
+ #[token("\\citetitle*")]
+ #[token("\\citeyear")]
+ #[token("\\citeyear*")]
+ #[token("\\citedate")]
+ #[token("\\citedate*")]
+ #[token("\\citeurl")]
+ #[token("\\fullcite")]
+ #[token("\\citeyearpar")]
+ #[token("\\citealt")]
+ #[token("\\citealp")]
+ #[token("\\citetext")]
+ #[token("\\parencite")]
+ #[token("\\parencite*")]
+ #[token("\\Parencite")]
+ #[token("\\footcite")]
+ #[token("\\footfullcite")]
+ #[token("\\footcitetext")]
+ #[token("\\textcite")]
+ #[token("\\Textcite")]
+ #[token("\\smartcite")]
+ #[token("\\supercite")]
+ #[token("\\autocite")]
+ #[token("\\autocite*")]
+ #[token("\\Autocite")]
+ #[token("\\Autocite*")]
+ #[token("\\volcite")]
+ #[token("\\Volcite")]
+ #[token("\\pvolcite")]
+ #[token("\\Pvolcite")]
+ #[token("\\fvolcite")]
+ #[token("\\ftvolcite")]
+ #[token("\\svolcite")]
+ #[token("\\Svolcite")]
+ #[token("\\tvolcite")]
+ #[token("\\Tvolcite")]
+ #[token("\\avolcite")]
+ #[token("\\Avolcite")]
+ #[token("\\notecite")]
+ #[token("\\pnotecite")]
+ #[token("\\Pnotecite")]
+ #[token("\\fnotecite")]
+ #[token("\\citeA")]
+ #[token("\\citeA*")]
+ Citation,
+
+ #[token("\\usepackage")]
+ #[token("\\RequirePackage")]
+ PackageInclude,
+
+ #[token("\\documentclass")]
+ ClassInclude,
+
+ #[token("\\include")]
+ #[token("\\subfileinclude")]
+ #[token("\\input")]
+ #[token("\\subfile")]
+ LatexInclude,
+
+ #[token("\\addbibresource")]
+ BiblatexInclude,
+
+ #[token("\\bibliography")]
+ BibtexInclude,
+
+ #[token("\\includegraphics")]
+ GraphicsInclude,
+
+ #[token("\\includesvg")]
+ SvgInclude,
+
+ #[token("\\includeinkscape")]
+ InkscapeInclude,
+
+ #[token("\\verbatiminput")]
+ #[token("\\VerbatimInput")]
+ VerbatimInclude,
+
+ #[token("\\import")]
+ #[token("\\subimport")]
+ #[token("\\inputfrom")]
+ #[token("\\subinputfrom")]
+ #[token("\\includefrom")]
+ #[token("\\subincludefrom")]
+ Import,
+
+ #[token("\\label")]
+ LabelDefinition,
+
+ #[token("\\ref")]
+ #[token("\\vref")]
+ #[token("\\Vref")]
+ #[token("\\autoref")]
+ #[token("\\pageref")]
+ #[token("\\cref")]
+ #[token("\\cref*")]
+ #[token("\\Cref")]
+ #[token("\\Cref*")]
+ #[token("\\namecref")]
+ #[token("\\nameCref")]
+ #[token("\\lcnamecref")]
+ #[token("\\namecrefs")]
+ #[token("\\nameCrefs")]
+ #[token("\\lcnamecrefs")]
+ #[token("\\labelcref")]
+ #[token("\\labelcpageref")]
+ #[token("\\eqref")]
+ LabelReference,
+
+ #[token("\\crefrange")]
+ #[token("\\crefrange*")]
+ #[token("\\Crefrange")]
+ #[token("\\Crefrange*")]
+ LabelReferenceRange,
+
+ #[token("\\newlabel")]
+ LabelNumber,
+
+ #[token("\\newcommand")]
+ #[token("\\newcommand*")]
+ #[token("\\renewcommand")]
+ #[token("\\renewcommand*")]
+ #[token("\\DeclareRobustCommand")]
+ #[token("\\DeclareRobustCommand*")]
+ CommandDefinition,
+
+ #[token("\\DeclareMathOperator")]
+ #[token("\\DeclareMathOperator*")]
+ MathOperator,
+
+ #[token("\\newglossaryentry")]
+ GlossaryEntryDefinition,
+
+ #[token("\\gls")]
+ #[token("\\Gls")]
+ #[token("\\GLS")]
+ #[token("\\glspl")]
+ #[token("\\Glspl")]
+ #[token("\\GLSpl")]
+ #[token("\\glsdisp")]
+ #[token("\\glslink")]
+ #[token("\\glstext")]
+ #[token("\\Glstext")]
+ #[token("\\GLStext")]
+ #[token("\\glsfirst")]
+ #[token("\\Glsfirst")]
+ #[token("\\GLSfirst")]
+ #[token("\\glsplural")]
+ #[token("\\Glsplural")]
+ #[token("\\GLSplural")]
+ #[token("\\glsfirstplural")]
+ #[token("\\Glsfirstplural")]
+ #[token("\\GLSfirstplural")]
+ #[token("\\glsname")]
+ #[token("\\Glsname")]
+ #[token("\\GLSname")]
+ #[token("\\glssymbol")]
+ #[token("\\Glssymbol")]
+ #[token("\\glsdesc")]
+ #[token("\\Glsdesc")]
+ #[token("\\GLSdesc")]
+ #[token("\\glsuseri")]
+ #[token("\\Glsuseri")]
+ #[token("\\GLSuseri")]
+ #[token("\\glsuserii")]
+ #[token("\\Glsuserii")]
+ #[token("\\glsuseriii")]
+ #[token("\\glsuseriv")]
+ #[token("\\Glsuseriv")]
+ #[token("\\GLSuseriv")]
+ #[token("\\glsuserv")]
+ #[token("\\Glsuserv")]
+ #[token("\\GLSuserv")]
+ #[token("\\glsuservi")]
+ #[token("\\Glsuservi")]
+ #[token("\\GLSuservi")]
+ GlossaryEntryReference,
+
+ #[token("\\newacronym")]
+ AcronymDefinition,
+
+ #[token("\\DeclareAcronym")]
+ AcronymDeclaration,
+
+ #[token("\\acrshort")]
+ #[token("\\Acrshort")]
+ #[token("\\ACRshort")]
+ #[token("\\acrshortpl")]
+ #[token("\\Acrshortpl")]
+ #[token("\\ACRshortpl")]
+ #[token("\\acrlong")]
+ #[token("\\Acrlong")]
+ #[token("\\ACRlong")]
+ #[token("\\acrlongpl")]
+ #[token("\\Acrlongpl")]
+ #[token("\\ACRlongpl")]
+ #[token("\\acrfull")]
+ #[token("\\Acrfull")]
+ #[token("\\ACRfull")]
+ #[token("\\acrfullpl")]
+ #[token("\\Acrfullpl")]
+ #[token("\\ACRfullpl")]
+ #[token("\\acs")]
+ #[token("\\Acs")]
+ #[token("\\acsp")]
+ #[token("\\Acsp")]
+ #[token("\\acl")]
+ #[token("\\Acl")]
+ #[token("\\aclp")]
+ #[token("\\Aclp")]
+ #[token("\\acf")]
+ #[token("\\Acf")]
+ #[token("\\acfp")]
+ #[token("\\Acfp")]
+ #[token("\\ac")]
+ #[token("\\Ac")]
+ #[token("\\acp")]
+ #[token("\\glsentrylong")]
+ #[token("\\Glsentrylong")]
+ #[token("\\glsentrylongpl")]
+ #[token("\\Glsentrylongpl")]
+ #[token("\\glsentryshort")]
+ #[token("\\Glsentryshort")]
+ #[token("\\glsentryshortpl")]
+ #[token("\\Glsentryshortpl")]
+ #[token("\\glsentryfullpl")]
+ #[token("\\Glsentryfullpl")]
+ AcronymReference,
+
+ #[token("\\newtheorem")]
+ #[token("\\newtheorem*")]
+ #[token("\\declaretheorem")]
+ #[token("\\declaretheorem*")]
+ TheoremDefinition,
+
+ #[token("\\color")]
+ #[token("\\colorbox")]
+ #[token("\\textcolor")]
+ #[token("\\pagecolor")]
+ ColorReference,
+
+ #[token("\\definecolor")]
+ ColorDefinition,
+
+ #[token("\\definecolorset")]
+ ColorSetDefinition,
+
+ #[token("\\usepgflibrary")]
+ #[token("\\usetikzlibrary")]
+ TikzLibraryImport,
+
+ #[token("\\newenvironment")]
+ #[token("\\newenvironment*")]
+ #[token("\\renewenvironment")]
+ #[token("\\renewenvironment*")]
+ EnvironmentDefinition,
+
+ #[token("\\graphicspath")]
+ GraphicsPath,
+
+ #[token("\\fi")]
+ EndBlockComment,
- #[regex(r"\\color|\\colorbox|\\textcolor|\\pagecolor")]
- COLOR_REFERENCE_NAME,
+ #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
+ #[error]
+ Generic,
+}
- #[regex(r"\\definecolor")]
- COLOR_DEFINITION_NAME,
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Logos)]
+enum BlockCommentToken {
+ #[token("\\fi")]
+ End,
- #[regex(r"\\definecolorset")]
- COLOR_SET_DEFINITION_NAME,
+ #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
+ #[regex(r"[^\\]+")]
+ #[error]
+ Verbatim,
+}
- #[regex(r"\\usepgflibrary|\\usetikzlibrary")]
- TIKZ_LIBRARY_IMPORT_NAME,
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord, Logos)]
+enum VerbatimEnvironmentToken {
+ #[token("\\end{asy}")]
+ #[token("\\end{verbatim}")]
+ #[token("\\end{lstlisting}")]
+ #[token("\\end{minted}")]
+ #[token("\\end{pycode}")]
+ End,
- #[regex(r"\\newenvironment|\\newenvironment*|\\renewenvironment|\\renewenvironment*")]
- ENVIRONMENT_DEFINITION_NAME,
+ #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")]
+ #[regex(r"[^\\]+")]
+ #[error]
+ Verbatim,
}
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -179,15 +420,9 @@ pub struct Lexer<'a> {
}
impl<'a> Lexer<'a> {
- pub fn new(text: &'a str) -> Self {
+ pub fn new(input: &'a str) -> Self {
let mut tokens = Vec::new();
- let mut lexer = Token::lexer(text);
- while let Some(kind) = lexer.next() {
- tokens.push((
- unsafe { std::mem::transmute::<Token, SyntaxKind>(kind) },
- lexer.slice(),
- ));
- }
+ tokenize(input, &mut tokens);
tokens.reverse();
Self { tokens }
}
@@ -201,6 +436,171 @@ impl<'a> Lexer<'a> {
}
}
+fn tokenize<'a>(input: &'a str, tokens: &mut Vec<(SyntaxKind, &'a str)>) {
+ let mut lexer = RootToken::lexer(input);
+ while let Some(kind) = lexer.next() {
+ let text = lexer.slice();
+ match kind {
+ RootToken::LineBreak => {
+ tokens.push((SyntaxKind::LINE_BREAK, text));
+ }
+ RootToken::Whitespace => {
+ tokens.push((SyntaxKind::WHITESPACE, text));
+ }
+ RootToken::LineComment => {
+ tokens.push((SyntaxKind::COMMENT, text));
+ }
+ RootToken::LCurly => {
+ tokens.push((SyntaxKind::L_CURLY, text));
+ }
+ RootToken::RCurly => {
+ tokens.push((SyntaxKind::R_CURLY, text));
+ }
+ RootToken::LBrack => {
+ tokens.push((SyntaxKind::L_BRACK, text));
+ }
+ RootToken::RBrack => {
+ tokens.push((SyntaxKind::R_BRACK, text));
+ }
+ RootToken::LParen => {
+ tokens.push((SyntaxKind::L_PAREN, text));
+ }
+ RootToken::RParen => {
+ tokens.push((SyntaxKind::R_PAREN, text));
+ }
+ RootToken::Comma => {
+ tokens.push((SyntaxKind::COMMA, text));
+ }
+ RootToken::Eq => {
+ tokens.push((SyntaxKind::EQUALITY_SIGN, text));
+ }
+ RootToken::Word => {
+ tokens.push((SyntaxKind::WORD, text));
+ }
+ RootToken::Dollar => {
+ tokens.push((SyntaxKind::DOLLAR, text));
+ }
+ RootToken::CommandName => {
+ let kind = tokenize_command_name(text);
+ tokens.push((kind, text));
+ }
+ RootToken::BeginBlockComment => {
+ tokens.push((SyntaxKind::BEGIN_BLOCK_COMMENT_NAME, text));
+ let end = lexer.span().end;
+ lexer = RootToken::lexer(tokenize_block_comment(&lexer.source()[end..], tokens));
+ }
+ RootToken::BeginVerbatimEnvironment => {
+ tokens.push((SyntaxKind::BEGIN_ENVIRONMENT_NAME, "\\begin"));
+ tokens.push((SyntaxKind::L_CURLY, "{"));
+ tokens.push((SyntaxKind::WORD, &text["\\begin{".len()..text.len() - 1]));
+ tokens.push((SyntaxKind::R_CURLY, "}"));
+ let end = lexer.span().end;
+ lexer = RootToken::lexer(tokenize_verbatim_environment(
+ &lexer.source()[end..],
+ tokens,
+ ));
+ }
+ }
+ }
+}
+
+fn tokenize_command_name(text: &str) -> SyntaxKind {
+ let mut lexer = CommandNameToken::lexer(text);
+ match lexer.next().unwrap() {
+ CommandNameToken::BeginEnvironment => SyntaxKind::BEGIN_ENVIRONMENT_NAME,
+ CommandNameToken::EndEnvironment => SyntaxKind::END_ENVIRONMENT_NAME,
+ CommandNameToken::BeginEquation => SyntaxKind::BEGIN_EQUATION_NAME,
+ CommandNameToken::EndEquation => SyntaxKind::END_EQUATION_NAME,
+ CommandNameToken::Part => SyntaxKind::PART_NAME,
+ CommandNameToken::Chapter => SyntaxKind::CHAPTER_NAME,
+ CommandNameToken::Section => SyntaxKind::SECTION_NAME,
+ CommandNameToken::Subsection => SyntaxKind::SUBSECTION_NAME,
+ CommandNameToken::Subsubsection => SyntaxKind::SUBSUBSECTION_NAME,
+ CommandNameToken::Paragraph => SyntaxKind::PARAGRAPH_NAME,
+ CommandNameToken::Subparagraph => SyntaxKind::SUBPARAGRAPH_NAME,
+ CommandNameToken::EnumItem => SyntaxKind::ENUM_ITEM_NAME,
+ CommandNameToken::Caption => SyntaxKind::CAPTION_NAME,
+ CommandNameToken::Citation => SyntaxKind::CITATION_NAME,
+ CommandNameToken::PackageInclude => SyntaxKind::PACKAGE_INCLUDE_NAME,
+ CommandNameToken::ClassInclude => SyntaxKind::CLASS_INCLUDE_NAME,
+ CommandNameToken::LatexInclude => SyntaxKind::LATEX_INCLUDE_NAME,
+ CommandNameToken::BiblatexInclude => SyntaxKind::BIBLATEX_INCLUDE_NAME,
+ CommandNameToken::BibtexInclude => SyntaxKind::BIBTEX_INCLUDE_NAME,
+ CommandNameToken::GraphicsInclude => SyntaxKind::GRAPHICS_INCLUDE_NAME,
+ CommandNameToken::SvgInclude => SyntaxKind::SVG_INCLUDE_NAME,
+ CommandNameToken::InkscapeInclude => SyntaxKind::INKSCAPE_INCLUDE_NAME,
+ CommandNameToken::VerbatimInclude => SyntaxKind::VERBATIM_INCLUDE_NAME,
+ CommandNameToken::Import => SyntaxKind::IMPORT_NAME,
+ CommandNameToken::LabelDefinition => SyntaxKind::LABEL_DEFINITION_NAME,
+ CommandNameToken::LabelReference => SyntaxKind::LABEL_REFERENCE_NAME,
+ CommandNameToken::LabelReferenceRange => SyntaxKind::LABEL_REFERENCE_RANGE_NAME,
+ CommandNameToken::LabelNumber => SyntaxKind::LABEL_NUMBER_NAME,
+ CommandNameToken::CommandDefinition => SyntaxKind::COMMAND_DEFINITION_NAME,
+ CommandNameToken::MathOperator => SyntaxKind::MATH_OPERATOR_NAME,
+ CommandNameToken::GlossaryEntryDefinition => SyntaxKind::GLOSSARY_ENTRY_DEFINITION_NAME,
+ CommandNameToken::GlossaryEntryReference => SyntaxKind::GLOSSARY_ENTRY_REFERENCE_NAME,
+ CommandNameToken::AcronymDefinition => SyntaxKind::ACRONYM_DEFINITION_NAME,
+ CommandNameToken::AcronymDeclaration => SyntaxKind::ACRONYM_DECLARATION_NAME,
+ CommandNameToken::AcronymReference => SyntaxKind::ACRONYM_REFERENCE_NAME,
+ CommandNameToken::TheoremDefinition => SyntaxKind::THEOREM_DEFINITION_NAME,
+ CommandNameToken::ColorReference => SyntaxKind::COLOR_REFERENCE_NAME,
+ CommandNameToken::ColorDefinition => SyntaxKind::COLOR_DEFINITION_NAME,
+ CommandNameToken::ColorSetDefinition => SyntaxKind::COLOR_SET_DEFINITION_NAME,
+ CommandNameToken::TikzLibraryImport => SyntaxKind::TIKZ_LIBRARY_IMPORT_NAME,
+ CommandNameToken::EnvironmentDefinition => SyntaxKind::ENVIRONMENT_DEFINITION_NAME,
+ CommandNameToken::EndBlockComment => SyntaxKind::END_BLOCK_COMMENT_NAME,
+ CommandNameToken::GraphicsPath => SyntaxKind::GRAPHICS_PATH_NAME,
+ CommandNameToken::Generic => SyntaxKind::GENERIC_COMMAND_NAME,
+ }
+}
+
+fn tokenize_block_comment<'a>(input: &'a str, tokens: &mut Vec<(SyntaxKind, &'a str)>) -> &'a str {
+ let mut lexer = BlockCommentToken::lexer(input);
+ let mut end = 0;
+ while let Some(kind) = lexer.next() {
+ match kind {
+ BlockCommentToken::Verbatim => {
+ end = lexer.span().end;
+ }
+ BlockCommentToken::End => {
+ end = lexer.span().start;
+ break;
+ }
+ };
+ }
+
+ if end > 0 {
+ tokens.push((SyntaxKind::VERBATIM, &input[..end]));
+ }
+
+ &input[end..]
+}
+
+fn tokenize_verbatim_environment<'a>(
+ input: &'a str,
+ tokens: &mut Vec<(SyntaxKind, &'a str)>,
+) -> &'a str {
+ let mut lexer = VerbatimEnvironmentToken::lexer(input);
+ let mut end = 0;
+ while let Some(kind) = lexer.next() {
+ match kind {
+ VerbatimEnvironmentToken::Verbatim => {
+ end = lexer.span().end;
+ }
+ VerbatimEnvironmentToken::End => {
+ end = lexer.span().start;
+ break;
+ }
+ };
+ }
+
+ if end > 0 {
+ tokens.push((SyntaxKind::VERBATIM, &input[..end]));
+ }
+
+ &input[end..]
+}
+
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;
@@ -257,4 +657,18 @@ mod tests {
fn test_line_break() {
assert_debug_snapshot!(verify("hello\nworld"));
}
+
+ #[test]
+ fn test_block_comment() {
+ assert_debug_snapshot!(verify("Foo\\iffalse\n\\Bar{Baz}\n\\fi\\Qux"));
+ }
+
+ #[test]
+ fn test_asymptote() {
+ assert_debug_snapshot!(verify(
+ r#"\begin{asy}
+ printf("Hello World\n");
+\end{asy}"#
+ ));
+ }
}
diff --git a/support/texlab/src/syntax/latex/parser.rs b/support/texlab/src/syntax/latex/parser.rs
index fcca6771a2..db9462874b 100644
--- a/support/texlab/src/syntax/latex/parser.rs
+++ b/support/texlab/src/syntax/latex/parser.rs
@@ -1,14 +1,13 @@
-use cstree::GreenNodeBuilder;
+use rowan::{GreenNode, GreenNodeBuilder};
use super::{
lexer::Lexer,
SyntaxKind::{self, *},
- SyntaxNode,
};
#[derive(Clone)]
pub struct Parse {
- pub root: SyntaxNode,
+ pub green: GreenNode,
}
#[derive(Debug, Clone, Copy)]
@@ -29,7 +28,7 @@ impl Default for ParserContext {
#[derive(Debug)]
struct Parser<'a> {
lexer: Lexer<'a>,
- builder: GreenNodeBuilder<'static, 'static>,
+ builder: GreenNodeBuilder<'static>,
}
impl<'a> Parser<'a> {
@@ -88,15 +87,13 @@ impl<'a> Parser<'a> {
self.content(ParserContext::default());
}
self.builder.finish_node();
- let (green_node, interner) = self.builder.finish();
- Parse {
- root: SyntaxNode::new_root_with_resolver(green_node, interner.unwrap()),
- }
+ let green = self.builder.finish();
+ Parse { green }
}
fn content(&mut self, context: ParserContext) {
match self.peek().unwrap() {
- LINE_BREAK | WHITESPACE | COMMENT => self.eat(),
+ LINE_BREAK | WHITESPACE | COMMENT | VERBATIM => self.eat(),
L_CURLY if context.allow_environment => self.curly_group(),
L_CURLY => self.curly_group_without_environments(),
L_BRACK | L_PAREN => self.mixed_group(),
@@ -151,7 +148,10 @@ impl<'a> Parser<'a> {
COLOR_DEFINITION_NAME => self.color_definition(),
COLOR_SET_DEFINITION_NAME => self.color_set_definition(),
TIKZ_LIBRARY_IMPORT_NAME => self.tikz_library_import(),
- ENVIRONMENT_DEFINIITION_NAME => self.environment_definition(),
+ ENVIRONMENT_DEFINITION_NAME => self.environment_definition(),
+ BEGIN_BLOCK_COMMENT_NAME => self.block_comment(),
+ END_BLOCK_COMMENT_NAME => self.generic_command(),
+ GRAPHICS_PATH_NAME => self.graphics_path(),
_ => unreachable!(),
}
}
@@ -186,6 +186,21 @@ impl<'a> Parser<'a> {
self.builder.finish_node();
}
+ fn curly_group_impl(&mut self) {
+ self.builder.start_node(CURLY_GROUP.into());
+ self.eat();
+ while let Some(kind) = self.peek() {
+ match kind {
+ R_CURLY => break,
+ BEGIN_ENVIRONMENT_NAME => self.begin(),
+ END_ENVIRONMENT_NAME => self.end(),
+ _ => self.content(ParserContext::default()),
+ };
+ }
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
fn curly_group_without_environments(&mut self) {
self.builder.start_node(CURLY_GROUP.into());
self.eat();
@@ -350,10 +365,15 @@ impl<'a> Parser<'a> {
fn value(&mut self) {
self.builder.start_node(VALUE.into());
- self.content(ParserContext {
- allow_environment: true,
- allow_comma: false,
- });
+ while let Some(kind) = self.lexer.peek() {
+ match kind {
+ COMMA | R_BRACK | R_CURLY => break,
+ _ => self.content(ParserContext {
+ allow_environment: true,
+ allow_comma: false,
+ }),
+ };
+ }
self.builder.finish_node();
}
@@ -760,6 +780,23 @@ impl<'a> Parser<'a> {
self.builder.finish_node();
}
+ fn block_comment(&mut self) {
+ self.builder.start_node(BLOCK_COMMENT.into());
+ self.eat();
+
+ if self.peek() == Some(VERBATIM) {
+ self.eat();
+ }
+
+ if self.peek() == Some(END_BLOCK_COMMENT_NAME) {
+ self.eat();
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.builder.finish_node();
+ }
+
fn caption(&mut self) {
self.builder.start_node(CAPTION.into());
self.eat();
@@ -806,7 +843,7 @@ impl<'a> Parser<'a> {
}
if self.lexer.peek() == Some(L_CURLY) {
- self.curly_group_word_list();
+ self.curly_group_path_list();
} else {
self.builder.token(MISSING.into(), "");
}
@@ -814,6 +851,57 @@ impl<'a> Parser<'a> {
self.builder.finish_node();
}
+ fn curly_group_path(&mut self) {
+ self.builder.start_node(CURLY_GROUP_WORD.into());
+ self.eat();
+ while matches!(self.peek(), Some(WORD)) {
+ self.path();
+ }
+
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
+ fn curly_group_path_list(&mut self) {
+ self.builder.start_node(CURLY_GROUP_WORD_LIST.into());
+ self.eat();
+
+ while self
+ .peek()
+ .filter(|&kind| {
+ matches!(
+ kind,
+ LINE_BREAK | WHITESPACE | COMMENT | WORD | COMMA | EQUALITY_SIGN
+ )
+ })
+ .is_some()
+ {
+ if self.peek() == Some(WORD) {
+ self.path();
+ } else {
+ self.eat();
+ }
+ }
+
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
+
+ fn path(&mut self) {
+ self.builder.start_node(KEY.into());
+ self.eat();
+ while self
+ .peek()
+ .filter(|&kind| matches!(kind, WHITESPACE | COMMENT | WORD | EQUALITY_SIGN))
+ .is_some()
+ {
+ self.eat();
+ }
+
+ self.trivia();
+ self.builder.finish_node();
+ }
+
fn package_include(&mut self) {
self.generic_include(PACKAGE_INCLUDE, true);
}
@@ -944,7 +1032,7 @@ impl<'a> Parser<'a> {
}
if self.lexer.peek() == Some(L_CURLY) {
- self.curly_group();
+ self.curly_group_impl();
} else {
self.builder.token(MISSING.into(), "");
}
@@ -964,7 +1052,7 @@ impl<'a> Parser<'a> {
}
if self.lexer.peek() == Some(L_CURLY) {
- self.curly_group();
+ self.curly_group_impl();
} else {
self.builder.token(MISSING.into(), "");
}
@@ -1195,6 +1283,9 @@ impl<'a> Parser<'a> {
if self.lexer.peek() == Some(L_BRACK) {
self.brack_group_word();
+ if self.lexer.peek() == Some(L_BRACK) {
+ self.brack_group();
+ }
}
for _ in 0..2 {
@@ -1207,6 +1298,26 @@ impl<'a> Parser<'a> {
self.builder.finish_node();
}
+
+ fn graphics_path(&mut self) {
+ self.builder.start_node(GRAPHICS_PATH.into());
+ self.eat();
+ self.trivia();
+
+ if self.lexer.peek() == Some(L_CURLY) {
+ self.eat();
+ self.trivia();
+
+ while matches!(self.lexer.peek(), Some(L_CURLY)) {
+ self.curly_group_path();
+ }
+ } else {
+ self.builder.token(MISSING.into(), "");
+ }
+
+ self.expect(R_CURLY);
+ self.builder.finish_node();
+ }
}
pub fn parse(text: &str) -> Parse {
@@ -1217,10 +1328,12 @@ pub fn parse(text: &str) -> Parse {
mod tests {
use insta::assert_debug_snapshot;
+ use crate::syntax::latex;
+
use super::*;
- fn setup(text: &str) -> SyntaxNode {
- parse(&text.trim().replace("\r", "")).root
+ fn setup(text: &str) -> latex::SyntaxNode {
+ latex::SyntaxNode::new_root(parse(&text.trim().replace('\r', "")).green)
}
#[test]
@@ -1460,6 +1573,11 @@ mod tests {
}
#[test]
+ fn test_latex_include_equality_sign() {
+ assert_debug_snapshot!(setup(r#"\include{foo=bar}"#));
+ }
+
+ #[test]
fn test_latex_input_simple() {
assert_debug_snapshot!(setup(r#"\input{foo/bar.tex}"#));
}
@@ -1490,6 +1608,11 @@ mod tests {
}
#[test]
+ fn test_graphics_include_complicated_options() {
+ assert_debug_snapshot!(setup(r#"\includegraphics[width=0.5\textwidth]{}"#));
+ }
+
+ #[test]
fn test_svg_include_simple() {
assert_debug_snapshot!(setup(r#"\includesvg{foo/bar.svg}"#));
}
@@ -1595,6 +1718,13 @@ mod tests {
}
#[test]
+ fn test_command_definition_with_begin() {
+ assert_debug_snapshot!(setup(
+ r#"\newcommand{\CVSubHeadingListStart}{\begin{itemize}[leftmargin=0.5cm, label={}]}"#
+ ));
+ }
+
+ #[test]
fn test_math_operator_simple() {
assert_debug_snapshot!(setup(r#"\DeclareMathOperator{\foo}{foo}"#));
}
@@ -1717,6 +1847,11 @@ mod tests {
}
#[test]
+ fn test_environment_definition_optional_arg() {
+ assert_debug_snapshot!(setup(r#"\newenvironment{foo}[1][default]{begin}{end}"#));
+ }
+
+ #[test]
fn test_acronym_declaration() {
assert_debug_snapshot!(setup(
r#"\DeclareAcronym{eg}{short = e.g,long = for example,tag = abbrev}"#
@@ -1727,4 +1862,33 @@ mod tests {
fn test_label_definition_line_break() {
assert_debug_snapshot!(setup("\\label{hello\nworld}"));
}
+
+ #[test]
+ fn test_block_comments() {
+ assert_debug_snapshot!(setup(
+ r#"Foo
+\iffalse
+Test1
+\fi
+Bar
+\iffalse
+\fii
+\fi
+Baz"#
+ ));
+ }
+
+ #[test]
+ fn test_asymptote() {
+ assert_debug_snapshot!(setup(
+ r#"\begin{asy}
+ printf("Hello World\n");
+\end{asy}"#
+ ));
+ }
+
+ #[test]
+ fn test_graphics_path() {
+ assert_debug_snapshot!(setup(r#"\graphicspath{{../figures/}}"#));
+ }
}
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__asymptote.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__asymptote.snap
new file mode 100644
index 0000000000..12f0f3ca63
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__asymptote.snap
@@ -0,0 +1,43 @@
+---
+source: src/syntax/latex/lexer.rs
+assertion_line: 664
+expression: "verify(r#\"\\begin{asy}\n printf(\"Hello World\\n\");\n\\end{asy}\"#)"
+---
+[
+ (
+ BEGIN_ENVIRONMENT_NAME,
+ "\\begin",
+ ),
+ (
+ L_CURLY,
+ "{",
+ ),
+ (
+ WORD,
+ "asy",
+ ),
+ (
+ R_CURLY,
+ "}",
+ ),
+ (
+ VERBATIM,
+ "\n printf(\"Hello World\\n\");\n",
+ ),
+ (
+ END_ENVIRONMENT_NAME,
+ "\\end",
+ ),
+ (
+ L_CURLY,
+ "{",
+ ),
+ (
+ WORD,
+ "asy",
+ ),
+ (
+ R_CURLY,
+ "}",
+ ),
+]
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__block_comment.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__block_comment.snap
new file mode 100644
index 0000000000..5ced043aaf
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__lexer__tests__block_comment.snap
@@ -0,0 +1,27 @@
+---
+source: src/syntax/latex/lexer.rs
+assertion_line: 601
+expression: "verify(\"Foo\\\\iffalse\\n\\\\Bar{Baz}\\n\\\\fi\\\\Qux\")"
+---
+[
+ (
+ WORD,
+ "Foo",
+ ),
+ (
+ BEGIN_BLOCK_COMMENT_NAME,
+ "\\iffalse",
+ ),
+ (
+ VERBATIM,
+ "\n\\Bar{Baz}\n",
+ ),
+ (
+ END_BLOCK_COMMENT_NAME,
+ "\\fi",
+ ),
+ (
+ GENERIC_COMMAND_NAME,
+ "\\Qux",
+ ),
+]
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__asymptote.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__asymptote.snap
new file mode 100644
index 0000000000..a735d71e73
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__asymptote.snap
@@ -0,0 +1,24 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1829
+expression: "setup(r#\"\\begin{asy}\n printf(\"Hello World\\n\");\n\\end{asy}\"#)"
+---
+ROOT@0..50
+ PREAMBLE@0..50
+ ENVIRONMENT@0..50
+ BEGIN@0..11
+ BEGIN_ENVIRONMENT_NAME@0..6 "\\begin"
+ CURLY_GROUP_WORD@6..11
+ L_CURLY@6..7 "{"
+ KEY@7..10
+ WORD@7..10 "asy"
+ R_CURLY@10..11 "}"
+ VERBATIM@11..41 "\n printf(\"Hello Wo ..."
+ END@41..50
+ END_ENVIRONMENT_NAME@41..45 "\\end"
+ CURLY_GROUP_WORD@45..50
+ L_CURLY@45..46 "{"
+ KEY@46..49
+ WORD@46..49 "asy"
+ R_CURLY@49..50 "}"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__block_comments.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__block_comments.snap
new file mode 100644
index 0000000000..355a93ebad
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__block_comments.snap
@@ -0,0 +1,26 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1814
+expression: "setup(r#\"Foo\n\\iffalse\nTest1\n\\fi\nBar\n\\iffalse\n\\fii\n\\fi\nBaz\"#)"
+---
+ROOT@0..48
+ PREAMBLE@0..48
+ TEXT@0..4
+ WORD@0..3 "Foo"
+ LINE_BREAK@3..4 "\n"
+ BLOCK_COMMENT@4..22
+ BEGIN_BLOCK_COMMENT_NAME@4..12 "\\iffalse"
+ VERBATIM@12..19 "\nTest1\n"
+ END_BLOCK_COMMENT_NAME@19..22 "\\fi"
+ LINE_BREAK@22..23 "\n"
+ TEXT@23..27
+ WORD@23..26 "Bar"
+ LINE_BREAK@26..27 "\n"
+ BLOCK_COMMENT@27..44
+ BEGIN_BLOCK_COMMENT_NAME@27..35 "\\iffalse"
+ VERBATIM@35..41 "\n\\fii\n"
+ END_BLOCK_COMMENT_NAME@41..44 "\\fi"
+ LINE_BREAK@44..45 "\n"
+ TEXT@45..48
+ WORD@45..48 "Baz"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap
new file mode 100644
index 0000000000..6b7724c0aa
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__command_definition_with_begin.snap
@@ -0,0 +1,39 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1723
+expression: "setup(r#\"\\newcommand{\\CVSubHeadingListStart}{\\begin{itemize}[leftmargin=0.5cm, label={}]}\"#)"
+---
+ROOT@0..80
+ PREAMBLE@0..80
+ COMMAND_DEFINITION@0..80
+ COMMAND_DEFINITION_NAME@0..11 "\\newcommand"
+ CURLY_GROUP_COMMAND@11..35
+ L_CURLY@11..12 "{"
+ GENERIC_COMMAND_NAME@12..34 "\\CVSubHeadingListStart"
+ R_CURLY@34..35 "}"
+ CURLY_GROUP@35..80
+ L_CURLY@35..36 "{"
+ BEGIN@36..79
+ BEGIN_ENVIRONMENT_NAME@36..42 "\\begin"
+ CURLY_GROUP_WORD@42..51
+ L_CURLY@42..43 "{"
+ KEY@43..50
+ WORD@43..50 "itemize"
+ R_CURLY@50..51 "}"
+ BRACK_GROUP@51..79
+ L_BRACK@51..52 "["
+ TEXT@52..62
+ WORD@52..62 "leftmargin"
+ EQUALITY_SIGN@62..63 "="
+ TEXT@63..75
+ WORD@63..68 "0.5cm"
+ COMMA@68..69 ","
+ WHITESPACE@69..70 " "
+ WORD@70..75 "label"
+ EQUALITY_SIGN@75..76 "="
+ CURLY_GROUP@76..78
+ L_CURLY@76..77 "{"
+ R_CURLY@77..78 "}"
+ R_BRACK@78..79 "]"
+ R_CURLY@79..80 "}"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition.snap
index 982d3b8667..5d977db814 100644
--- a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition.snap
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition.snap
@@ -1,12 +1,12 @@
---
source: src/syntax/latex/parser.rs
+assertion_line: 1792
expression: "setup(r#\"\\newenvironment{bar}[1]{\\begin{foo}}{\\end{foo}}\"#)"
-
---
ROOT@0..47
PREAMBLE@0..47
ENVIRONMENT_DEFINITION@0..47
- ENVIRONMENT_DEFINIITION_NAME@0..15 "\\newenvironment"
+ ENVIRONMENT_DEFINITION_NAME@0..15 "\\newenvironment"
CURLY_GROUP_WORD@15..20
L_CURLY@15..16 "{"
KEY@16..19
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition_optional_arg.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition_optional_arg.snap
new file mode 100644
index 0000000000..1a064fefe3
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__environment_definition_optional_arg.snap
@@ -0,0 +1,35 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1797
+expression: "setup(r#\"\\newenvironment{foo}[1][default]{begin}{end}\"#)"
+---
+ROOT@0..44
+ PREAMBLE@0..44
+ ENVIRONMENT_DEFINITION@0..44
+ ENVIRONMENT_DEFINITION_NAME@0..15 "\\newenvironment"
+ CURLY_GROUP_WORD@15..20
+ L_CURLY@15..16 "{"
+ KEY@16..19
+ WORD@16..19 "foo"
+ R_CURLY@19..20 "}"
+ BRACK_GROUP_WORD@20..23
+ L_BRACK@20..21 "["
+ KEY@21..22
+ WORD@21..22 "1"
+ R_BRACK@22..23 "]"
+ BRACK_GROUP@23..32
+ L_BRACK@23..24 "["
+ TEXT@24..31
+ WORD@24..31 "default"
+ R_BRACK@31..32 "]"
+ CURLY_GROUP@32..39
+ L_CURLY@32..33 "{"
+ TEXT@33..38
+ WORD@33..38 "begin"
+ R_CURLY@38..39 "}"
+ CURLY_GROUP@39..44
+ L_CURLY@39..40 "{"
+ TEXT@40..43
+ WORD@40..43 "end"
+ R_CURLY@43..44 "}"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_include_complicated_options.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_include_complicated_options.snap
new file mode 100644
index 0000000000..a79c7f925e
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_include_complicated_options.snap
@@ -0,0 +1,27 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1547
+expression: "setup(r#\"\\includegraphics[width=0.5\\textwidth]{}\"#)"
+
+---
+ROOT@0..39
+ PREAMBLE@0..39
+ GRAPHICS_INCLUDE@0..39
+ GRAPHICS_INCLUDE_NAME@0..16 "\\includegraphics"
+ BRACK_GROUP_KEY_VALUE@16..37
+ L_BRACK@16..17 "["
+ KEY_VALUE_BODY@17..36
+ KEY_VALUE_PAIR@17..36
+ KEY@17..22
+ WORD@17..22 "width"
+ EQUALITY_SIGN@22..23 "="
+ VALUE@23..36
+ TEXT@23..26
+ WORD@23..26 "0.5"
+ GENERIC_COMMAND@26..36
+ GENERIC_COMMAND_NAME@26..36 "\\textwidth"
+ R_BRACK@36..37 "]"
+ CURLY_GROUP_WORD_LIST@37..39
+ L_CURLY@37..38 "{"
+ R_CURLY@38..39 "}"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_path.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_path.snap
new file mode 100644
index 0000000000..622a0fc869
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__graphics_path.snap
@@ -0,0 +1,17 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1871
+expression: "setup(r#\"\\graphicspath{{../figures/}}\"#)"
+---
+ROOT@0..28
+ PREAMBLE@0..28
+ GRAPHICS_PATH@0..28
+ GRAPHICS_PATH_NAME@0..13 "\\graphicspath"
+ L_CURLY@13..14 "{"
+ CURLY_GROUP_WORD@14..27
+ L_CURLY@14..15 "{"
+ KEY@15..26
+ WORD@15..26 "../figures/"
+ R_CURLY@26..27 "}"
+ R_CURLY@27..28 "}"
+
diff --git a/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__latex_include_equality_sign.snap b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__latex_include_equality_sign.snap
new file mode 100644
index 0000000000..c6cfb5dda3
--- /dev/null
+++ b/support/texlab/src/syntax/latex/snapshots/texlab__syntax__latex__parser__tests__latex_include_equality_sign.snap
@@ -0,0 +1,18 @@
+---
+source: src/syntax/latex/parser.rs
+assertion_line: 1504
+expression: "setup(r#\"\\include{foo=bar}\"#)"
+
+---
+ROOT@0..17
+ PREAMBLE@0..17
+ LATEX_INCLUDE@0..17
+ LATEX_INCLUDE_NAME@0..8 "\\include"
+ CURLY_GROUP_WORD_LIST@8..17
+ L_CURLY@8..9 "{"
+ KEY@9..16
+ WORD@9..12 "foo"
+ EQUALITY_SIGN@12..13 "="
+ WORD@13..16 "bar"
+ R_CURLY@16..17 "}"
+
diff --git a/support/texlab/src/uri.rs b/support/texlab/src/uri.rs
deleted file mode 100644
index b1ae9073e7..0000000000
--- a/support/texlab/src/uri.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use std::{
- fmt,
- hash::{Hash, Hasher},
- ops::Deref,
- path::Path,
-};
-
-use serde::{Deserialize, Serialize};
-use url::{ParseError, Url};
-
-#[derive(Eq, Clone, Serialize, Deserialize)]
-pub struct Uri(Url);
-
-impl Uri {
- pub fn with_extension(&self, extension: &str) -> Option<Self> {
- let file_name = self.path_segments()?.last()?;
- let file_stem = match file_name.rfind('.') {
- Some(index) => &file_name[..index],
- None => file_name,
- };
- self.join(&format!("{}.{}", file_stem, extension))
- .ok()
- .map(Into::into)
- }
-
- pub fn parse(input: &str) -> Result<Self, ParseError> {
- Url::parse(input).map(|url| url.into())
- }
-
- pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> {
- Url::from_directory_path(path).map(|url| url.into())
- }
-
- pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> {
- Url::from_file_path(path).map(|url| url.into())
- }
-}
-
-impl PartialEq for Uri {
- fn eq(&self, other: &Self) -> bool {
- if cfg!(windows) {
- self.as_str().to_lowercase() == other.as_str().to_lowercase()
- } else {
- self.as_str() == other.as_str()
- }
- }
-}
-
-impl Hash for Uri {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.as_str().to_lowercase().hash(state);
- }
-}
-
-impl Deref for Uri {
- type Target = Url;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl From<Url> for Uri {
- fn from(url: Url) -> Self {
- Uri(url)
- }
-}
-
-impl Into<Url> for Uri {
- fn into(self) -> Url {
- self.0
- }
-}
-
-impl fmt::Debug for Uri {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl fmt::Display for Uri {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
diff --git a/support/texlab/src/workspace.rs b/support/texlab/src/workspace.rs
index 12924e2cf4..8fda9c7c1b 100644
--- a/support/texlab/src/workspace.rs
+++ b/support/texlab/src/workspace.rs
@@ -1,32 +1,216 @@
-mod api;
-mod children_expand;
-mod document;
-mod parent_expand;
-mod storage;
-mod watch;
-
-use std::sync::Arc;
+use std::{fs, path::PathBuf, sync::Arc};
use anyhow::Result;
+use crossbeam_channel::Sender;
+use lsp_types::Url;
+use petgraph::{graphmap::UnGraphMap, visit::Dfs};
+use rustc_hash::{FxHashMap, FxHashSet};
-use crate::ServerContext;
+use crate::{component_db::COMPONENT_DATABASE, Document, DocumentLanguage, Environment};
-pub use self::{api::*, document::*};
-use self::{
- children_expand::ChildrenExpander, parent_expand::ParentExpander, storage::Storage,
- watch::DocumentWatcher,
-};
+#[derive(Debug, Clone)]
+pub enum WorkspaceEvent {
+ Changed(Workspace, Document),
+}
-pub fn create_workspace_fast(context: Arc<ServerContext>) -> Result<impl Workspace> {
- let workspace = Storage::new(context);
- let workspace = ChildrenExpander::new(Arc::new(workspace));
- Ok(workspace)
+#[derive(Debug, Clone, Default)]
+pub struct Workspace {
+ pub documents_by_uri: FxHashMap<Arc<Url>, Document>,
+ pub viewport: FxHashSet<Arc<Url>>,
+ pub listeners: Vec<Sender<WorkspaceEvent>>,
+ pub environment: Environment,
}
-pub fn create_workspace_full(context: Arc<ServerContext>) -> Result<impl Workspace> {
- let workspace = Storage::new(context);
- let workspace = DocumentWatcher::new(Arc::new(workspace))?;
- let workspace = ParentExpander::new(workspace);
- let workspace = ChildrenExpander::new(Arc::new(workspace));
- Ok(workspace)
+impl Workspace {
+ pub fn new(environment: Environment) -> Self {
+ Self {
+ environment,
+ ..Self::default()
+ }
+ }
+
+ pub fn open(
+ &mut self,
+ uri: Arc<Url>,
+ text: Arc<String>,
+ language: DocumentLanguage,
+ ) -> Result<Document> {
+ log::debug!("(Re)Loading document: {}", uri);
+ let document = Document::parse(&self.environment, Arc::clone(&uri), text, language);
+
+ self.documents_by_uri
+ .insert(Arc::clone(&uri), document.clone());
+
+ for listener in &self.listeners {
+ listener.send(WorkspaceEvent::Changed(self.clone(), document.clone()))?;
+ }
+
+ self.expand_parent(&document);
+ self.expand_children(&document);
+ Ok(document)
+ }
+
+ pub fn reload(&mut self, path: PathBuf) -> Result<Option<Document>> {
+ let uri = Arc::new(Url::from_file_path(path.clone()).unwrap());
+
+ if self.is_open(&uri) && !uri.as_str().ends_with(".log") {
+ return Ok(self.documents_by_uri.get(&uri).cloned());
+ }
+
+ if let Some(language) = DocumentLanguage::by_path(&path) {
+ let data = fs::read(&path)?;
+ let text = Arc::new(String::from_utf8_lossy(&data).into_owned());
+ Ok(Some(self.open(uri, text, language)?))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn load(&mut self, path: PathBuf) -> Result<Option<Document>> {
+ let uri = Arc::new(Url::from_file_path(path.clone()).unwrap());
+
+ if let Some(document) = self.documents_by_uri.get(&uri).cloned() {
+ return Ok(Some(document));
+ }
+
+ let data = fs::read(&path)?;
+ let text = Arc::new(String::from_utf8_lossy(&data).into_owned());
+ if let Some(language) = DocumentLanguage::by_path(&path) {
+ Ok(Some(self.open(uri, text, language)?))
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn close(&mut self, uri: &Url) {
+ self.viewport.remove(uri);
+ }
+
+ pub fn is_open(&self, uri: &Url) -> bool {
+ self.viewport.contains(uri)
+ }
+
+ pub fn slice(&self, uri: &Url) -> Self {
+ let all_uris: Vec<_> = self.documents_by_uri.keys().cloned().collect();
+
+ all_uris
+ .iter()
+ .position(|u| u.as_ref() == uri)
+ .map(|start| {
+ let mut edges = Vec::new();
+ for (i, uri) in all_uris.iter().enumerate() {
+ let document = self.documents_by_uri.get(uri);
+ if let Some(data) = document
+ .as_ref()
+ .and_then(|document| document.data.as_latex())
+ {
+ let extras = &data.extras;
+ let mut all_targets =
+ vec![&extras.implicit_links.aux, &extras.implicit_links.log];
+ for link in &extras.explicit_links {
+ all_targets.push(&link.targets);
+ }
+
+ for targets in all_targets {
+ for target in targets {
+ if let Some(j) = all_uris.iter().position(|uri| uri == target) {
+ edges.push((i, j, ()));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ let mut slice = self.clone();
+ slice.documents_by_uri = FxHashMap::default();
+ let graph = UnGraphMap::from_edges(edges);
+ let mut dfs = Dfs::new(&graph, start);
+ while let Some(i) = dfs.next(&graph) {
+ let uri = &all_uris[i];
+ let doc = self.documents_by_uri[uri].clone();
+ slice.documents_by_uri.insert(Arc::clone(uri), doc);
+ }
+
+ slice
+ })
+ .unwrap_or_default()
+ }
+
+ fn find_parent(&self, uri: &Url) -> Option<Document> {
+ self.slice(uri)
+ .documents_by_uri
+ .values()
+ .find(|document| {
+ document.data.as_latex().map_or(false, |data| {
+ data.extras.has_document_environment
+ && !data
+ .extras
+ .explicit_links
+ .iter()
+ .filter_map(|link| link.as_component_name())
+ .any(|name| name == "subfiles.cls")
+ })
+ })
+ .cloned()
+ }
+
+ fn expand_parent(&mut self, document: &Document) {
+ let all_current_paths = self
+ .documents_by_uri
+ .values()
+ .filter_map(|doc| doc.uri.to_file_path().ok())
+ .collect::<FxHashSet<_>>();
+
+ if document.uri.scheme() == "file" {
+ if let Ok(mut path) = document.uri.to_file_path() {
+ while path.pop() && self.find_parent(&document.uri).is_none() {
+ std::fs::read_dir(&path)
+ .into_iter()
+ .flatten()
+ .filter_map(|entry| entry.ok())
+ .filter(|entry| entry.file_type().ok().filter(|ty| ty.is_file()).is_some())
+ .map(|entry| entry.path())
+ .filter(|path| {
+ matches!(
+ DocumentLanguage::by_path(path),
+ Some(DocumentLanguage::Latex)
+ )
+ })
+ .filter(|path| !all_current_paths.contains(path))
+ .for_each(|path| {
+ let _ = self.load(path);
+ });
+ }
+ }
+ }
+ }
+
+ fn expand_children(&mut self, document: &Document) {
+ if let Some(data) = document.data.as_latex() {
+ let extras = &data.extras;
+ let mut all_targets = vec![&extras.implicit_links.aux, &extras.implicit_links.log];
+ for link in &extras.explicit_links {
+ if link
+ .as_component_name()
+ .and_then(|name| COMPONENT_DATABASE.find(&name))
+ .is_none()
+ {
+ all_targets.push(&link.targets);
+ }
+ }
+
+ all_targets.into_iter().for_each(|targets| {
+ for path in targets
+ .iter()
+ .filter(|uri| uri.scheme() == "file" && uri.fragment().is_none())
+ .filter_map(|uri| uri.to_file_path().ok())
+ {
+ if self.load(path).is_ok() {
+ break;
+ }
+ }
+ });
+ }
+ }
}
diff --git a/support/texlab/src/workspace/api.rs b/support/texlab/src/workspace/api.rs
deleted file mode 100644
index 6a3c3d02c9..0000000000
--- a/support/texlab/src/workspace/api.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use std::{fs, path::PathBuf, sync::Arc};
-
-use anyhow::Result;
-use notify::RecursiveMode;
-
-use crate::{DocumentLanguage, Uri};
-
-use super::Document;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
-pub enum WorkspaceSource {
- Client,
- Server,
-}
-
-#[derive(Debug, Clone)]
-pub struct WorkspaceSubset {
- pub documents: Vec<Arc<Document>>,
-}
-
-pub type OpenHandler = Arc<dyn Fn(Arc<dyn Workspace>, Arc<Document>) + Send + Sync + 'static>;
-
-pub trait Workspace: Send + Sync {
- fn open(
- &self,
- uri: Arc<Uri>,
- text: String,
- language: DocumentLanguage,
- source: WorkspaceSource,
- ) -> Arc<Document>;
-
- fn register_open_handler(&self, handler: OpenHandler);
-
- fn reload(&self, path: PathBuf) -> Result<Option<Arc<Document>>> {
- let uri = Arc::new(Uri::from_file_path(path.clone()).unwrap());
-
- if self.is_open(&uri) && !uri.as_str().ends_with(".log") {
- return Ok(self.get(&uri));
- }
-
- if let Some(language) = DocumentLanguage::by_path(&path) {
- let data = fs::read(&path)?;
- let text = String::from_utf8_lossy(&data).into_owned();
- Ok(Some(self.open(
- uri,
- text,
- language,
- WorkspaceSource::Server,
- )))
- } else {
- Ok(None)
- }
- }
-
- fn load(&self, path: PathBuf) -> Result<Option<Arc<Document>>> {
- let uri = Arc::new(Uri::from_file_path(path.clone()).unwrap());
-
- if let Some(document) = self.get(&uri) {
- return Ok(Some(document));
- }
-
- let data = fs::read(&path)?;
- let text = String::from_utf8_lossy(&data).into_owned();
- if let Some(language) = DocumentLanguage::by_path(&path) {
- Ok(Some(self.open(
- uri,
- text,
- language,
- WorkspaceSource::Server,
- )))
- } else {
- Ok(None)
- }
- }
-
- fn documents(&self) -> Vec<Arc<Document>>;
-
- fn has(&self, uri: &Uri) -> bool;
-
- fn get(&self, uri: &Uri) -> Option<Arc<Document>>;
-
- fn close(&self, uri: &Uri);
-
- fn is_open(&self, uri: &Uri) -> bool;
-
- fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset>;
-
- fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()>;
-}
diff --git a/support/texlab/src/workspace/children_expand.rs b/support/texlab/src/workspace/children_expand.rs
deleted file mode 100644
index 8fecf2d503..0000000000
--- a/support/texlab/src/workspace/children_expand.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use std::{path::PathBuf, sync::Arc};
-
-use anyhow::Result;
-use notify::RecursiveMode;
-use rayon::iter::{IntoParallelIterator, ParallelIterator};
-
-use crate::{
- component_db::COMPONENT_DATABASE, Document, DocumentLanguage, OpenHandler, Uri, Workspace,
- WorkspaceSource, WorkspaceSubset,
-};
-
-pub struct ChildrenExpander<W> {
- workspace: Arc<W>,
-}
-
-impl<W> ChildrenExpander<W>
-where
- W: Workspace + Send + Sync + 'static,
-{
- pub fn new(workspace: Arc<W>) -> Self {
- workspace.register_open_handler(Arc::new(move |workspace, document| {
- Self::expand(workspace.as_ref(), document.as_ref());
- }));
- Self { workspace }
- }
-
- fn expand(workspace: &dyn Workspace, document: &Document) {
- if let Some(data) = document.data.as_latex() {
- let extras = &data.extras;
- let mut all_targets = vec![&extras.implicit_links.aux, &extras.implicit_links.log];
- for link in &extras.explicit_links {
- if link
- .as_component_name()
- .and_then(|name| COMPONENT_DATABASE.find(&name))
- .is_none()
- {
- all_targets.push(&link.targets);
- }
- }
-
- all_targets.into_par_iter().for_each(|targets| {
- for path in targets
- .iter()
- .filter(|uri| uri.scheme() == "file" && uri.fragment().is_none())
- .filter_map(|uri| uri.to_file_path().ok())
- {
- if workspace.load(path).is_ok() {
- break;
- }
- }
- });
- }
- }
-}
-
-impl<W: Workspace> Workspace for ChildrenExpander<W> {
- fn open(
- &self,
- uri: Arc<Uri>,
- text: String,
- language: DocumentLanguage,
- source: WorkspaceSource,
- ) -> Arc<Document> {
- self.workspace.open(uri, text, language, source)
- }
-
- fn register_open_handler(&self, handler: OpenHandler) {
- self.workspace.register_open_handler(handler)
- }
-
- fn documents(&self) -> Vec<Arc<Document>> {
- self.workspace.documents()
- }
-
- fn has(&self, uri: &Uri) -> bool {
- self.workspace.has(uri)
- }
-
- fn get(&self, uri: &Uri) -> Option<Arc<Document>> {
- self.workspace.get(uri)
- }
-
- fn close(&self, uri: &Uri) {
- self.workspace.close(uri)
- }
-
- fn is_open(&self, uri: &Uri) -> bool {
- self.workspace.is_open(uri)
- }
-
- fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> {
- self.workspace.subset(uri)
- }
-
- fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> {
- self.workspace.watch(path, mode)
- }
-}
diff --git a/support/texlab/src/workspace/parent_expand.rs b/support/texlab/src/workspace/parent_expand.rs
deleted file mode 100644
index 0eef06ce92..0000000000
--- a/support/texlab/src/workspace/parent_expand.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use std::{fs, path::PathBuf, sync::Arc};
-
-use anyhow::Result;
-use notify::RecursiveMode;
-use rayon::iter::{IntoParallelIterator, ParallelIterator};
-use rustc_hash::FxHashSet;
-
-use crate::{
- Document, DocumentLanguage, OpenHandler, Uri, Workspace, WorkspaceSource, WorkspaceSubset,
-};
-
-pub struct ParentExpander<W> {
- workspace: W,
-}
-
-impl<W: Workspace> ParentExpander<W> {
- pub fn new(workspace: W) -> Self {
- Self { workspace }
- }
-}
-
-impl<W> Workspace for ParentExpander<W>
-where
- W: Workspace + Send + Sync + 'static,
-{
- fn open(
- &self,
- uri: Arc<Uri>,
- text: String,
- language: DocumentLanguage,
- source: WorkspaceSource,
- ) -> Arc<Document> {
- let document = self
- .workspace
- .open(Arc::clone(&uri), text, language, source);
-
- let all_current_paths = self
- .workspace
- .documents()
- .into_iter()
- .filter_map(|doc| doc.uri.to_file_path().ok())
- .collect::<FxHashSet<_>>();
-
- if uri.scheme() == "file" {
- if let Ok(mut path) = uri.to_file_path() {
- while path.pop() && !self.has_parent(Arc::clone(&uri)).unwrap_or(false) {
- let mut files = Vec::new();
- fs::read_dir(&path)
- .into_iter()
- .flatten()
- .filter_map(|entry| entry.ok())
- .filter(|entry| entry.file_type().ok().filter(|ty| ty.is_file()).is_some())
- .map(|entry| entry.path())
- .filter(|path| {
- matches!(
- DocumentLanguage::by_path(&path),
- Some(DocumentLanguage::Latex)
- )
- })
- .filter(|path| !all_current_paths.contains(path))
- .for_each(|path| {
- files.push(path);
- });
- files.into_par_iter().for_each(|path| {
- let _ = self.workspace.load(path);
- });
- }
- }
- }
-
- document
- }
-
- fn register_open_handler(&self, handler: OpenHandler) {
- self.workspace.register_open_handler(handler)
- }
-
- fn documents(&self) -> Vec<Arc<Document>> {
- self.workspace.documents()
- }
-
- fn has(&self, uri: &Uri) -> bool {
- self.workspace.has(uri)
- }
-
- fn get(&self, uri: &Uri) -> Option<Arc<Document>> {
- self.workspace.get(uri)
- }
-
- fn close(&self, uri: &Uri) {
- self.workspace.close(uri)
- }
-
- fn is_open(&self, uri: &Uri) -> bool {
- self.workspace.is_open(uri)
- }
-
- fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> {
- self.workspace.subset(uri)
- }
-
- fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> {
- self.workspace.watch(path, mode)
- }
-}
-
-impl<W> ParentExpander<W>
-where
- W: Workspace + Send + Sync + 'static,
-{
- fn has_parent(&self, uri: Arc<Uri>) -> Option<bool> {
- let subset = self.subset(Arc::clone(&uri))?;
- Some(subset.documents.iter().any(|document| {
- document
- .data
- .as_latex()
- .map(|data| {
- data.extras.has_document_environment
- && !data
- .extras
- .explicit_links
- .iter()
- .filter_map(|link| link.as_component_name())
- .any(|name| name == "subfiles.cls")
- })
- .unwrap_or(false)
- }))
- }
-}
diff --git a/support/texlab/src/workspace/storage.rs b/support/texlab/src/workspace/storage.rs
deleted file mode 100644
index 5f067a16aa..0000000000
--- a/support/texlab/src/workspace/storage.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use std::{
- path::PathBuf,
- sync::{Arc, Mutex},
-};
-
-use anyhow::Result;
-use notify::RecursiveMode;
-use petgraph::{graphmap::UnGraphMap, visit::Dfs};
-use rustc_hash::{FxHashMap, FxHashSet};
-
-use crate::{
- Document, DocumentLanguage, OpenHandler, ServerContext, Uri, Workspace, WorkspaceSource,
- WorkspaceSubset,
-};
-
-#[derive(Clone)]
-pub struct Storage {
- context: Arc<ServerContext>,
- documents_by_uri: Arc<Mutex<FxHashMap<Arc<Uri>, Arc<Document>>>>,
- opened_documents: Arc<Mutex<FxHashSet<Arc<Uri>>>>,
- open_handlers: Arc<Mutex<Vec<OpenHandler>>>,
-}
-
-impl Storage {
- pub fn new(context: Arc<ServerContext>) -> Self {
- Self {
- context,
- documents_by_uri: Arc::default(),
- opened_documents: Arc::default(),
- open_handlers: Arc::default(),
- }
- }
-}
-
-impl Workspace for Storage {
- fn open(
- &self,
- uri: Arc<Uri>,
- text: String,
- language: DocumentLanguage,
- source: WorkspaceSource,
- ) -> Arc<Document> {
- log::debug!("(Re)Loading document: {}", uri);
- let document = Arc::new(Document::parse(
- Arc::clone(&self.context),
- Arc::clone(&uri),
- text,
- language,
- ));
- {
- self.documents_by_uri
- .lock()
- .unwrap()
- .insert(Arc::clone(&uri), Arc::clone(&document));
- }
-
- if source == WorkspaceSource::Client {
- self.opened_documents.lock().unwrap().insert(uri);
- }
-
- let handlers = { self.open_handlers.lock().unwrap().clone() };
- for handler in handlers {
- handler(Arc::new(self.clone()), Arc::clone(&document));
- }
-
- document
- }
-
- fn register_open_handler(&self, handler: OpenHandler) {
- self.open_handlers.lock().unwrap().push(handler);
- }
-
- fn documents(&self) -> Vec<Arc<Document>> {
- self.documents_by_uri
- .lock()
- .unwrap()
- .values()
- .cloned()
- .collect()
- }
-
- fn has(&self, uri: &Uri) -> bool {
- self.documents_by_uri.lock().unwrap().contains_key(uri)
- }
-
- fn get(&self, uri: &Uri) -> Option<Arc<Document>> {
- self.documents_by_uri.lock().unwrap().get(uri).cloned()
- }
-
- fn close(&self, uri: &Uri) {
- self.opened_documents.lock().unwrap().remove(uri);
- }
-
- fn is_open(&self, uri: &Uri) -> bool {
- self.opened_documents.lock().unwrap().contains(uri)
- }
-
- fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> {
- let all_current_uris: Vec<Arc<Uri>> = self
- .documents_by_uri
- .lock()
- .unwrap()
- .keys()
- .cloned()
- .collect();
-
- let mut edges = Vec::new();
- for (i, uri) in all_current_uris.iter().enumerate() {
- let document = self.get(uri);
- if let Some(data) = document
- .as_ref()
- .and_then(|document| document.data.as_latex())
- {
- let extras = &data.extras;
- let mut all_targets = vec![&extras.implicit_links.aux, &extras.implicit_links.log];
- for link in &extras.explicit_links {
- all_targets.push(&link.targets);
- }
-
- for targets in all_targets {
- for target in targets {
- if let Some(j) = all_current_uris.iter().position(|uri| uri == target) {
- edges.push((i, j, ()));
- break;
- }
- }
- }
- }
- }
-
- let graph = UnGraphMap::from_edges(edges);
- let start = all_current_uris.iter().position(|u| *u == uri)?;
- let mut dfs = Dfs::new(&graph, start);
- let mut documents = Vec::new();
- while let Some(i) = dfs.next(&graph) {
- documents.push(self.get(&all_current_uris[i])?);
- }
-
- Some(WorkspaceSubset { documents })
- }
-
- fn watch(&self, _path: PathBuf, _mode: RecursiveMode) -> Result<()> {
- Ok(())
- }
-}
diff --git a/support/texlab/src/workspace/watch.rs b/support/texlab/src/workspace/watch.rs
deleted file mode 100644
index 4cc1834620..0000000000
--- a/support/texlab/src/workspace/watch.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use std::{
- path::PathBuf,
- sync::{Arc, Mutex},
-};
-
-use anyhow::Result;
-use log::warn;
-use notify::{RecommendedWatcher, RecursiveMode, Watcher};
-use rustc_hash::FxHashSet;
-
-use crate::{
- Document, DocumentLanguage, OpenHandler, Uri, Workspace, WorkspaceSource, WorkspaceSubset,
-};
-
-pub struct DocumentWatcher<W> {
- workspace: Arc<W>,
- watcher: Mutex<RecommendedWatcher>,
- watched_paths: Mutex<FxHashSet<PathBuf>>,
-}
-
-impl<W> DocumentWatcher<W>
-where
- W: Workspace + Send + Sync + 'static,
-{
- pub fn new(workspace: Arc<W>) -> Result<Self> {
- let watcher = Self::create_watcher(Arc::clone(&workspace))?;
- Ok(Self {
- workspace,
- watcher: Mutex::new(watcher),
- watched_paths: Mutex::default(),
- })
- }
-
- fn create_watcher(workspace: Arc<W>) -> Result<RecommendedWatcher> {
- let watcher = notify::recommended_watcher(move |event: notify::Result<notify::Event>| {
- if let Ok(event) = event {
- if event.kind.is_modify() {
- for path in event.paths {
- let _ = workspace.reload(path);
- }
- }
- }
- })?;
- Ok(watcher)
- }
-}
-
-impl<W: Workspace> Workspace for DocumentWatcher<W> {
- fn open(
- &self,
- uri: Arc<Uri>,
- text: String,
- language: DocumentLanguage,
- source: WorkspaceSource,
- ) -> Arc<Document> {
- let document = self.workspace.open(uri, text, language, source);
- if document.uri.scheme() == "file" {
- if let Ok(mut path) = document.uri.to_file_path() {
- path.pop();
- if let Err(why) = self.watch(path, RecursiveMode::NonRecursive) {
- warn!(
- "Failed to watch folder of document \"{}\": {}",
- document.uri, why
- );
- }
- }
- }
- document
- }
-
- fn register_open_handler(&self, handler: OpenHandler) {
- self.workspace.register_open_handler(handler);
- }
-
- fn documents(&self) -> Vec<Arc<Document>> {
- self.workspace.documents()
- }
-
- fn has(&self, uri: &Uri) -> bool {
- self.workspace.has(uri)
- }
-
- fn get(&self, uri: &Uri) -> Option<Arc<Document>> {
- self.workspace.get(uri)
- }
-
- fn close(&self, uri: &Uri) {
- self.workspace.close(uri)
- }
-
- fn is_open(&self, uri: &Uri) -> bool {
- self.workspace.is_open(uri)
- }
-
- fn subset(&self, uri: Arc<Uri>) -> Option<WorkspaceSubset> {
- self.workspace.subset(uri)
- }
-
- fn watch(&self, path: PathBuf, mode: RecursiveMode) -> Result<()> {
- let mut watched_paths = self.watched_paths.lock().unwrap();
- if !watched_paths.contains(&path) {
- self.watcher.lock().unwrap().watch(&path, mode)?;
- watched_paths.insert(path);
- }
-
- Ok(())
- }
-}
diff --git a/support/texlab/tests/integration/common.rs b/support/texlab/tests/it/common.rs
index 5430bfd2be..5430bfd2be 100644
--- a/support/texlab/tests/integration/common.rs
+++ b/support/texlab/tests/it/common.rs
diff --git a/support/texlab/tests/integration/completion.rs b/support/texlab/tests/it/completion.rs
index eccc8162ba..58ad1970ba 100644
--- a/support/texlab/tests/integration/completion.rs
+++ b/support/texlab/tests/it/completion.rs
@@ -257,7 +257,7 @@ mod latex {
server.open(
"main.bib",
r#"
- @article{foo 2019,
+ @article{foo,
author = {Foo Bar},
title = {Baz Qux},
year = {2019},
diff --git a/support/texlab/tests/integration/document_symbol.rs b/support/texlab/tests/it/document_symbol.rs
index dbdbe64972..dbdbe64972 100644
--- a/support/texlab/tests/integration/document_symbol.rs
+++ b/support/texlab/tests/it/document_symbol.rs
diff --git a/support/texlab/tests/integration/hover.rs b/support/texlab/tests/it/hover.rs
index 70b14cccd0..70b14cccd0 100644
--- a/support/texlab/tests/integration/hover.rs
+++ b/support/texlab/tests/it/hover.rs
diff --git a/support/texlab/tests/integration/issues.rs b/support/texlab/tests/it/issues.rs
index 1fb66dc4a9..1fb66dc4a9 100644
--- a/support/texlab/tests/integration/issues.rs
+++ b/support/texlab/tests/it/issues.rs
diff --git a/support/texlab/tests/integration/main.rs b/support/texlab/tests/it/main.rs
index ebccca7396..ebccca7396 100644
--- a/support/texlab/tests/integration/main.rs
+++ b/support/texlab/tests/it/main.rs
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__command_complete_entry.snap
index acd3389f49..8f3aa3a4b2 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__command_complete_entry.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 79
expression: "complete_and_resolve(&server, uri, 1, 18)?"
-
---
{
"isIncomplete": true,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__command_incomplete_entry.snap
index acd3389f49..272e527920 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__command_incomplete_entry.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 61
expression: "complete_and_resolve(&server, uri, 1, 18)?"
-
---
{
"isIncomplete": true,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__empty_document.snap
index 7f8638455c..339ed48749 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__empty_document.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 34
expression: "complete_and_resolve(&server, uri, 0, 0)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_complete.snap
index fdc29dacb4..2bc50f8505 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_complete.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 143
expression: "complete_and_resolve(&server, uri, 0, 1)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name.snap
index ad571951ed..ddc3f29f5f 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name.snap
@@ -1,14 +1,18 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 95
expression: "complete_and_resolve(&server, uri, 0, 1)?"
-
---
{
"isIncomplete": true,
"items": [
{
- "label": "preamble",
+ "label": "article",
"kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An article in a journal, magazine, newspaper, or other periodical which forms a \n self-contained unit with its own title. The title of the periodical is given in the \n journaltitle field. If the issue has its own title in addition to the main title of \n the periodical, it goes in the issuetitle field. Note that editor and related \n fields refer to the journal while translator and related fields refer to the article.\n\nRequired fields: `author`, `title`, `journaltitle`, `year/date`"
+ },
"preselect": false,
"sortText": "00",
"textEdit": {
@@ -22,12 +26,16 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "preamble"
+ "newText": "article"
}
},
{
- "label": "string",
+ "label": "artwork",
"kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Works of the visual arts such as paintings, sculpture, and installations."
+ },
"preselect": false,
"sortText": "01",
"textEdit": {
@@ -41,12 +49,16 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "string"
+ "newText": "artwork"
}
},
{
- "label": "comment",
+ "label": "audio",
"kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Audio recordings, typically on audio cd, dvd, audio cassette, or similar media. See\n also `@music`."
+ },
"preselect": false,
"sortText": "02",
"textEdit": {
@@ -60,15 +72,15 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "comment"
+ "newText": "audio"
}
},
{
- "label": "article",
+ "label": "bibnote",
"kind": 1,
"documentation": {
"kind": "markdown",
- "value": "An article in a journal, magazine, newspaper, or other periodical which forms a \n self-contained unit with its own title. The title of the periodical is given in the \n journaltitle field. If the issue has its own title in addition to the main title of \n the periodical, it goes in the issuetitle field. Note that editor and related \n fields refer to the journal while translator and related fields refer to the article.\n\nRequired fields: `author`, `title`, `journaltitle`, `year/date`"
+ "value": "This special entry type is not meant to be used in the `bib` file like other types. It is\n provided for third-party packages like `notes2bib` which merge notes into the bibliography.\n The notes should go into the `note` field. Be advised that the `@bibnote`\n type is not related to the `defbibnote` command in any way. `defbibnote`\n is for adding comments at the beginning or the end of the bibliography, whereas\n the `@bibnote` type is meant for packages which render endnotes as bibliography\n entries."
},
"preselect": false,
"sortText": "03",
@@ -83,7 +95,7 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "article"
+ "newText": "bibnote"
}
},
{
@@ -110,11 +122,11 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
}
},
{
- "label": "mvbook",
+ "label": "bookinbook",
"kind": 1,
"documentation": {
"kind": "markdown",
- "value": "A multi-volume `@book`. For backwards compatibility, multi-volume books are also\n supported by the entry type `@book`. However, it is advisable to make use of the\n dedicated entry type `@mvbook`.\n\nRequired fields: `author`, `title`, `year/date`"
+ "value": "This type is similar to `@inbook` but intended for works originally published as a\n stand-alone book. A typical example are books reprinted in the collected works of\n an author."
},
"preselect": false,
"sortText": "05",
@@ -129,15 +141,15 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "mvbook"
+ "newText": "bookinbook"
}
},
{
- "label": "inbook",
+ "label": "booklet",
"kind": 1,
"documentation": {
"kind": "markdown",
- "value": "A part of a book which forms a self-contained unit with its own title. Note that the\n profile of this entry type is different from standard BibTeX.\n\nRequired fields: `author`, `title`, `booktitle`, `year/date`"
+ "value": "A book-like work without a formal publisher or sponsoring institution. Use the field\n howpublished to supply publishing information in free format, if applicable. The\n field type may be useful as well.\n\nRequired fields: `author/editor`, `title`, `year/date`"
},
"preselect": false,
"sortText": "06",
@@ -152,7 +164,7 @@ expression: "complete_and_resolve(&server, uri, 0, 1)?"
"character": 1
}
},
- "newText": "inbook"
+ "newText": "booklet"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name_before.snap
index 7f8638455c..b6cf4a29dd 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_empty_name_before.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 111
expression: "complete_and_resolve(&server, uri, 0, 0)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_incomplete.snap
index 2f3236bfc4..2e7226985d 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__entry_type_incomplete.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 127
expression: "complete_and_resolve(&server, uri, 0, 1)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__field_complete_entry.snap
index 8c6f330647..9340866462 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__field_complete_entry.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 178
expression: "complete_and_resolve(&server, uri, 1, 6)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__field_incomplete_entry.snap
index 72e803cd36..398ed710ce 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__field_incomplete_entry.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 160
expression: "complete_and_resolve(&server, uri, 1, 6)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap b/support/texlab/tests/it/snapshots/it__completion__bibtex__junk.snap
index 7f8638455c..72c29cf065 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__bibtex__junk.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 43
expression: "complete_and_resolve(&server, uri, 0, 0)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap b/support/texlab/tests/it/snapshots/it__completion__latex__begin_command.snap
index 9bfe6e82f8..ff1fed0a8d 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__begin_command.snap
@@ -1,32 +1,12 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 203
expression: "complete_and_resolve(&server, uri, 0, 2)?"
-
---
{
"isIncomplete": true,
"items": [
{
- "label": "begin",
- "kind": 1,
- "detail": "built-in",
- "preselect": false,
- "sortText": "00",
- "textEdit": {
- "range": {
- "start": {
- "line": 0,
- "character": 1
- },
- "end": {
- "line": 0,
- "character": 2
- }
- },
- "newText": "begin"
- }
- },
- {
"label": "b",
"kind": 1,
"detail": "built-in",
@@ -35,7 +15,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"value": "![b](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAA6CAIAAAB5+022AAAHLklEQVR4nO2ZX0hTbRzHz9nZ5jb2V8O5mqaOWrrUCJZgLqmkhBDroghSCYr+UDdGXRQFRnQTBUU3o4tjRGgJQZg4Jkgs5pIkM4uZtmzqnJa5s7X/5+w8570477v3cJzbMe19veh7+dvzfM/nnOd3fs/vPIMpioLWknj/NwBbf4Ayac0B8TmOi8Vi4+PjdrvdZrN9/Phxbm4uFAoBAMRicU5Ojl6vr66u3r17d3l5uUwmWwkQnPEti0ajdrv90aNHNpttbm4OhmGVSqVSqSQSCUmSGIbNz89Ho1Eej5ednW00GhsbG+vq6rKzs3+RiFpaAACXy3Xq1CmVSoUgiE6nO336dGdn58jIiNfrXVhYmJ+fd7lcXV1dzc3NSQKxWLx///6+vj6CINKYL6UlgQAAb9++3blzJ4/HU6vVV65c+fTp01LXiEajz58/NxgMyfvUaDT37t0Lh8OrBjQxMVFdXQ3DsMFg6OnpwXE8vREAwG636/X6JJNMJrtx40YkElkFIBzHL1y4gCBIVlZWS0vL0NBQNBrN6EWSZHt7u1wuTzLJ5fIHDx4kEomVAo2Ojm7cuJE2FQgEGo3m/Pnzs7OzGe1CodDhw4eZOarT6YaGhlYK9PjxY4FAwPTl8/ktLS3xeDyjY3d3t1QqTU6EYfjMmTNcJtJKXRg/f/5MEAQzkkgkurq6PB5PxtfWaDRu3ryZ+RZbLBaXy5VxIq3UQNFodHHQ7/f7/f6Mjjk5Odu3b2dGvF7vwMDAioB0Oh2fzy7iCoVCoVBkdEQQpLy8nMf715kgiHfv3gEAfh1o7969JSUlrMvU1dVptVoupgUFBawUnJqaYuXAkkqZWSRJ9vb2VlZWisViBEEUCsXRo0fdbjfHxHz58iUzryEIqq2tDYVCXOam3lx5PF5tbe3WrVuHh4cxDNNqtdu2bWMWmPQSCoXMJYMgCIZhGIa5zF1yt4dhWKPRaDQajhCsuayIQCBgIS4biIsAALFYLBgMBgIBv9//8+fPUCgUi8XoXe8/AiII4sePHy6X68OHDyMjIy6Xa25uDsOwcDiM4zhJkuAfMWdxXK9lAJEk6fV6+/v7rVbr4ODg9PQ03aBBECQUClUqVXFxcX5+vlarzcvL8/l8ZrM5Fost61a5AhEE4XQ629vbu7u7v3z5Eo/H6bhIJNLpdCaTqaamxmAwrF+/XiqVCoVCGIZfv36NoujqA1EUNTk5aTabOzo6PB5PchWkUqnJZGpsbKypqVGr1YtL6Iq0VD3AcfzFixdGoxFBkORgBEF27Njx5MkTv9+fppY4HA5WjTh48CDH/TU1UDweN5vNarWaaSoSiU6ePDkxMQEASG+6ykCJRAJFUVaXLhKJLl26hGEYF9NVBnI4HMnujBaPx2tubvb5fFwcVwjEzsdgMHjnzp2pqSlmUK/XX758WaVSpU/HZenv5wFBrILJBrLb7X19fRTjYw1BkKamJmbPtSr6+vXr/fv3KYq6e/cuM84u58+ePWN1YXl5efX19RwLPy0cxzN2P9++fUNRFEVRVpx9mf7+flZEr9cXFhZyp4EgCMMwrt3PIrGBZmZmWJHi4mKxWLws07GxMRzHVwcoEomwIjKZjPvWSDs4HA7qVw/m2ECs1hOCIAzDOLbDtEZHR9+8ecMK0t+KzAhBECRJZgbatGkTK+J0Orl8bNDCcfzhw4c+n2/Lli3M5xoMBlmLSOfZ4n2QDXTkyBHWoNHR0cWZnlIURVmt1o6OjsLCwrNnzzIzz+v1+nw+5sixsTGCIIqKilK4MDU5OVlVVcUaU1NTMz09nb7CAgAGBgYMBoNIJLp9+7bb7WYehkgkks7OzuTgQCCwb98+BEFaW1tZPmwgAIDNZmOVQQRBjh07loYJx3GLxVJWViYQCE6cOIFhWCKRaG1tZT5sk8nkdDoJggiHw2azWSqVlpWVjY+PZwCiKIokyVevXlVXVzPt+Hz+nj17LBZLIBBg7vaRSGR4ePjixYu5ublSqfTcuXPfv3+nf3K73VVVVclM4vF4JSUlTU1N9fX1SqVy3bp1T58+Xdw4pG4/AAAej+fmzZsGg0EoFCaxlEplbW3ttWvXUBRta2u7fv16Q0PDhg0bsrKyKioqUBRlfnzRi1hZWcnsqCAIgmG4qKiora0t5ZlTujNGkiQ9Ho/NZuvt7X3//v3s7Gw4HCYIAgBA7yQIgiiVytLS0kOHDjU0NBQUFLAqFkVRExMTKIparVb6NCc3N3fXrl3Hjx+vqKhggf6NmwYoKRzHFxYWZmZmvF6v1+sNBAKxWEwsFufn5+v1ep1OJ5fL0xRPkiR9Ph/dvahUqpycnDRdLyeg/1Jr7uD8D1AmrTmg1NkeiUR+uaFJL6FQKJFIlgcEALh161ZPT8/vADpw4MDVq1dTVqAlgehqNjg4+DuASktL0xea1EuWnZ2dn5//O4Ay/kv0pzBm0h+gTFpzQH8Bp4gu0MowthIAAAAASUVORK5CYII=|width=48,height=48)"
},
"preselect": false,
- "sortText": "01",
+ "sortText": "00",
"textEdit": {
"range": {
"start": {
@@ -59,7 +39,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"value": "![backslash](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAA6CAIAAAAsmq7oAAADD0lEQVR4nO2Y3Uv6UBzGv5stsyhCiUTRytJIDTMkRUEqhmnbVTf+o9sygqjAi4RIVqlJKWWl1FB6oyLt/C68K9tb5+YHncudw4fnczjbcxiBEALcg8RO/IN+ha6srKTT6ZeXF5xQSZJ2d3fPzs5wQjc2Nlqt1vb29m+O2lcoy7IWiyWTyTw+PmKDer3eUCgkiuLp6Sk26NDQEMMwT09PW1tb+ncAfRuFQsHhcEQiEUmSvs+qGT3O6dTUVDgcLhQKoijqC9oDOjAwwLLs29vb5ubm5+enHmrP/Ofn5y6XKxgMNhoNPPoA4HA4YrFYuVw+PDzUEbQ3tL+/n2GYTqcjCEK73dZM/Unh8vJydnbW5/PVajU8+gBgs9ni8XilUsnlclqD/gjt6+tjWZYgCI7jPj4+tFFlLG5ububn52dmZiqVCh59ABgfH19dXa3VatlsVlNQOajBYFhfX6coiuf59/d3DVR5kbu7u1AoNDExUSqV8OgDgMVioWn69vZ2f39ffVAFKEmSyWRycHCQ5/nX11e1VEWXZrMZjUZtNpsoinj0AWB0dHRtbe3+/n5nZwep+2wrQwmCSCQSIyMjgiCorG5Vlwmv1xsIBI6OjkqlEjbo8PBwMplUX92qoARB0DTdre6Hhwc8UADweDyLi4vHx8dqqlsttFvdz8/PmUxGubjUv3wnJyd2uz0cDitWt4arpMvlWlpaKhaL+XxefqUGqMlk6la3IAidTkduqXp9hFC5XJ6cnFxYWKjX63j0AcDpdEajUcXq1gY1Go3d6uY4Tq66NekjhKrVqtvtnpubu7q6wqMPAHa7PR6PV6vVg4ODn9ZohlIUxTCMQnVr1UcIXV9f+3y+6enpi4sLPPoAYLVal5eXZapbD9RgMDAMQ1EUx3G9q1uHPkKo0WgEg0Gn01ksFvHoA8DY2BhN0/V6fW9v7/usTihJkqlUymQy8Tzfo7j06SOEJEmKRCJWqzWXy+HRBwCz2ZxIJCRJ4nkeW1KEUDabNZvNfr8fW1IA8Pv9sVjMaDR+eU6gvx8z/wX0H6vA4+khaPAcAAAAAElFTkSuQmCC|width=48,height=48)"
},
"preselect": false,
- "sortText": "02",
+ "sortText": "01",
"textEdit": {
"range": {
"start": {
@@ -79,7 +59,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"kind": 1,
"detail": "built-in",
"preselect": false,
- "sortText": "03",
+ "sortText": "02",
"textEdit": {
"range": {
"start": {
@@ -103,7 +83,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"value": "![bar](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAA6CAIAAACvXpfTAAAHHklEQVR4nO2ZX0xSbxjHORxAkH8FEShCpU7DRI3RNHEul1uzrdRqtpazumrzprV1QReuedFFrbY229ja6kJbK7fanEsrK1saWGoFREA0Y0mioAiNA8HhnPO7YDudH3Dg6K/t14Xfu/fhfZ7zed/3Oc/7vgcAwzDaXyP6/w3wL23QkOvvoqFhJLp16xaLxQL/tM6cOZNIJMgeyshCiSAIgiB/dvAoimb5lZRGq9X29vb+8fe/qqqKTidND2Cj3pBqg4ZcGzTk2qAhF2n1oyIURSEI8vv9gUAgFovl5+fLZDKJRMJgrDPsOt0ikYjFYhkdHZ2YmHC73aFQCEEQJpMpk8n2799/+vRptVoNguBaw665FsMwbDKZDAbDixcvVlZWQBDcsWNHVVWVRCIJBoNWq9Xlcsnl8kuXLp04cYLJZGIY9vXr16GhoaWlpfr6+paWFjabTRqdbDvNKI/Ho9frt27dSqPR6HS6SqXq6+tzu92xWAxFURiGPR7PtWvXZDKZWCweGBhIJBIfP37UarXJvUkoFN6/fz9LfKo0CIKYTKampqbk/DMYjPb2dpvNhiBISk8Yhu/duycWi4uLiycnJ7u6ugAAwAd//vx5FEX/E00ikRgeHi4rK0vGZTAYXV1dCwsLZP1jsZher2cwGI2NjYWFhb/TAgCuXr2a5UG5aRAEefjwoVKpxCO2trZ6vd7sXk6ns6SkhE6nEyeGy+WOjY2tnwZF0adPn27fvh2PqFarP3/+nHMMMAx3d3en5Ghpaanb7V4/jdlsrqysxMMJBILBwcEsC0/Uo0eP8vLyiDStra3RaHSdND6f79ChQ/hUAwDQ1dUFQRAVFAzDZmdnxWIxMWmuXLmS3YWUBobhy5cvs1gsPJxUKjUajRRRMAwbGxvjcrm4O5/PHx8fz+5Cuk9NT08bDIZ4PI5bmpqaampqyPqnCMOw9+/fRyIR3FJUVFRWVpbdKzMNBEF9fX0/fvzALRwO5+jRoxwOhyJNLBZLTiRuUavVW7ZsWQ/N69evR0dHibFKSkr27t1LEYVGo/l8PpvN9vsxdLpOpyOuO1WaaDTa398fDAaJxsbGRqlUSp3G4XAsLCzgTYFAoNVqc3ploLFarS9fviRaOBxOc3Pzms4Jb9++jUajeFOpVJaUlKyZJlnulpeXiUa5XL57927qKNFodGpqirjQNTU1IpFozTSrq6vPnj1LuZ9WV1fLZDLqNF6v1263400QBHU6HZPJXDONy+VyOp3/6kGnNzQ0ZDuUpMluty8uLuLNTZs2UZzaVJqZmZnV1VWihcfjUUlAXBiGmUymX79+4RalUrlt27b10ExPTycSCaJFLpcXFxdTp4Eg6N27d8Skqays3Lx583povnz5kmIpLy8nbjc5tbCwQFxrEAT37NlDJWky0Ph8vhSLWq1O2Yqzy2azEYPw+XyNRkPRN5UGgiBik8lkqtVq6igoihqNxlgshlsyVhoURefm5lJeF1rOGwyXy6WYgEmFw+GZmRli0lRXV6cv9NLS0qlTpwKBAHH3oKXPTcqbLBQKJRIJdRqPx+NyufAmCIJ1dXXpSWO3281mc/qFK5UmeTvBJRAIeDwedRqLxeL3+4nu6ZUGw7CpqSkIgnQ6XQ4alUpFbHI4nJwbL654PP78+XPikUipVKZXh0gkMjExkZeXt2/fvhw0DQ0NxIml/lkUwzCj0Tg6Oko0qlSq9ErjdrutVqtCoUgvqqk09fX1crkcb4ZCoZS3jEwOh0Ov13u9XtwCAEBFRUXK1GIYNjk56fP5tFptUVFRDprS0tLm5mb8ZL68vOzxeHKieDyeCxcuzM7OEucVAADiwJKCIGh4eBgAgAMHDmQoY+lH5Tdv3uA7Np1Ov379evYry/z8/LFjx1gsVltbW0dHBx4ZBMG7d++mdH716pVIJNq5c+e3b9/SQ2WgicfjPT09+AzX1dUtLi5m5EBR1GKxtLS0sFisw4cPu93ux48fCwQCfCQGg4HYPxwOd3Z2giB48eLFjN/3M99gfD7f8ePHk/WAxWL19PSkX6OCwWB/f/+uXbvYbPbJkyfn5+cxDAuFQh0dHfhCd3d3wzCc7A/D8J07d/h8vlqtdjqdGZ9Lep+an5/v7OxMFkM+n3/u3LkPHz74/f7l5WWHwzEwMHDw4EEejyeVSnt7e1dWVnDHT58+1dbWJoEUCsXQ0JDf7//+/fvNmzcLCgokEsmDBw/Sv2zkoMEwLBAI3Lhxo6KigslkgiBYUFCg0Wg0Go1CoWCz2SKR6MiRI+Pj47FYjOiFoqjVam1vb8/Pz6fRaCKRSKPRlJeXczgcpVJ5+/btlP5E5fi2hSDI3NzcyMjIkydPHA7Hz58/QRAsLCzU6XRtbW21tbV4lqQoEAiMjIwMDg6azeZwOCwUChsaGs6ePVtbW5vltL/xrwe5NmjI9XfR/ANs1iTDg+2GBQAAAABJRU5ErkJggg==|width=48,height=48)"
},
"preselect": false,
- "sortText": "04",
+ "sortText": "03",
"textEdit": {
"range": {
"start": {
@@ -123,7 +103,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"kind": 1,
"detail": "built-in",
"preselect": false,
- "sortText": "05",
+ "sortText": "04",
"textEdit": {
"range": {
"start": {
@@ -143,7 +123,7 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
"kind": 1,
"detail": "built-in",
"preselect": false,
- "sortText": "06",
+ "sortText": "05",
"textEdit": {
"range": {
"start": {
@@ -157,6 +137,26 @@ expression: "complete_and_resolve(&server, uri, 0, 2)?"
},
"newText": "baselinestretch"
}
+ },
+ {
+ "label": "batchmode",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "batchmode"
+ }
}
]
}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap b/support/texlab/tests/it/snapshots/it__completion__latex__citation.snap
index ef6c57e882..af2249cb0a 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__citation.snap
@@ -1,21 +1,17 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 237
expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
-
---
{
"isIncomplete": false,
"items": [
{
- "label": "foo:2019",
+ "label": "bar:2005",
"kind": 1,
- "documentation": {
- "kind": "markdown",
- "value": "Bar, Foo. (2019). *Baz Qux*."
- },
"preselect": false,
- "sortText": "00 foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
- "filterText": "foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
+ "sortText": "00 bar:2005 @articlebar:2005",
+ "filterText": "bar:2005 @articlebar:2005",
"textEdit": {
"range": {
"start": {
@@ -27,15 +23,19 @@ expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
"character": 6
}
},
- "newText": "foo:2019"
+ "newText": "bar:2005"
}
},
{
- "label": "bar:2005",
+ "label": "foo:2019",
"kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "F. Bar: \"Baz Qux\". (2019)."
+ },
"preselect": false,
- "sortText": "01 bar:2005 @articlebar:2005",
- "filterText": "bar:2005 @articlebar:2005",
+ "sortText": "01 foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
+ "filterText": "foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
"textEdit": {
"range": {
"start": {
@@ -47,7 +47,7 @@ expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
"character": 6
}
},
- "newText": "bar:2005"
+ "newText": "foo:2019"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap b/support/texlab/tests/it/snapshots/it__completion__latex__citation_after.snap
index 156b28dc02..9d635a36d3 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__citation_after.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 305
expression: "complete_and_resolve(&server, tex_uri, 3, 7)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_multi_word.snap b/support/texlab/tests/it/snapshots/it__completion__latex__citation_multi_word.snap
index dd27d5d1a5..94ed4922d6 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_multi_word.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__citation_multi_word.snap
@@ -1,21 +1,21 @@
---
-source: tests/integration/completion.rs
-expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
-
+source: tests/it/completion.rs
+assertion_line: 271
+expression: "complete_and_resolve(&server, tex_uri, 3, 7)?"
---
{
"isIncomplete": false,
"items": [
{
- "label": "foo 2019",
+ "label": "foo",
"kind": 1,
"documentation": {
"kind": "markdown",
- "value": "Bar, Foo. (2019). *Baz Qux*."
+ "value": "F. Bar: \"Baz Qux\". (2019)."
},
"preselect": false,
- "sortText": "00 foo 2019 @articlefoo 2019 author Foo Bar title Baz Qux year 2019",
- "filterText": "foo 2019 @articlefoo 2019 author Foo Bar title Baz Qux year 2019",
+ "sortText": "00 foo @articlefoo author Foo Bar title Baz Qux year 2019",
+ "filterText": "foo @articlefoo author Foo Bar title Baz Qux year 2019",
"textEdit": {
"range": {
"start": {
@@ -27,7 +27,7 @@ expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
"character": 11
}
},
- "newText": "foo 2019"
+ "newText": "foo"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap b/support/texlab/tests/it/snapshots/it__completion__latex__citation_open_brace.snap
index d05a96929d..43012267d5 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__citation_open_brace.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 337
expression: "complete_and_resolve(&server, tex_uri, 3, 9)?"
-
---
{
"isIncomplete": false,
@@ -11,7 +11,7 @@ expression: "complete_and_resolve(&server, tex_uri, 3, 9)?"
"kind": 1,
"documentation": {
"kind": "markdown",
- "value": "Bar, Foo. (2019). *Baz Qux*."
+ "value": "F. Bar: \"Baz Qux\". (2019)."
},
"preselect": false,
"sortText": "00 FooBar @articleFooBar author Foo Bar title Baz Qux year 2019",
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap b/support/texlab/tests/it/snapshots/it__completion__latex__class_command.snap
index ff214e764a..bac91f2943 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__class_command.snap
@@ -1,13 +1,13 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 483
expression: "complete_and_resolve(&server, uri, 2, 5)?"
-
---
{
"isIncomplete": false,
"items": [
{
- "label": "chaptername",
+ "label": "chapter",
"kind": 1,
"detail": "book.cls",
"preselect": false,
@@ -23,7 +23,7 @@ expression: "complete_and_resolve(&server, uri, 2, 5)?"
"character": 5
}
},
- "newText": "chaptername"
+ "newText": "chapter"
}
},
{
@@ -47,7 +47,7 @@ expression: "complete_and_resolve(&server, uri, 2, 5)?"
}
},
{
- "label": "chapter",
+ "label": "chaptername",
"kind": 1,
"detail": "book.cls",
"preselect": false,
@@ -63,7 +63,7 @@ expression: "complete_and_resolve(&server, uri, 2, 5)?"
"character": 5
}
},
- "newText": "chapter"
+ "newText": "chaptername"
}
},
{
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__class_environment.snap
index c893498077..b751033521 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__class_environment.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 506
expression: "complete_and_resolve(&server, uri, 4, 13)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap b/support/texlab/tests/it/snapshots/it__completion__latex__class_import.snap
index baf1218114..7d964d6f85 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__class_import.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 569
expression: "complete_and_resolve(&server, uri, 0, 19)?"
-
---
{
"isIncomplete": false,
@@ -122,11 +122,11 @@ expression: "complete_and_resolve(&server, uri, 0, 19)?"
}
},
{
- "label": "caesar_book",
+ "label": "biditufte-book",
"kind": 1,
"documentation": {
"kind": "plaintext",
- "value": "The package allows typesetting of texts with notes, figures, citations, captions and tables in the margin. This is common (for example) in science text books."
+ "value": "A convenient interface for typesetting bidirectional texts with plain TeX and LaTeX. The package includes adaptations for use with many other commonly-used packages."
},
"preselect": false,
"sortText": "05",
@@ -141,15 +141,15 @@ expression: "complete_and_resolve(&server, uri, 0, 19)?"
"character": 19
}
},
- "newText": "caesar_book"
+ "newText": "biditufte-book"
}
},
{
- "label": "tufte-book",
+ "label": "caesar_book",
"kind": 1,
"documentation": {
"kind": "plaintext",
- "value": "Provided are two classes inspired, respectively, by handouts and books created by Edward Tufte."
+ "value": "The package allows typesetting of texts with notes, figures, citations, captions and tables in the margin. This is common (for example) in science text books."
},
"preselect": false,
"sortText": "06",
@@ -164,7 +164,7 @@ expression: "complete_and_resolve(&server, uri, 0, 19)?"
"character": 19
}
},
- "newText": "tufte-book"
+ "newText": "caesar_book"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap b/support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color.snap
index 5385f5c646..d65f573d96 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color.snap
@@ -1,13 +1,13 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 373
expression: "complete_and_resolve(&server, uri, 1, 18)?"
-
---
{
"isIncomplete": false,
"items": [
{
- "label": "gray",
+ "label": "HTML",
"kind": 1,
"preselect": false,
"sortText": "00",
@@ -22,11 +22,11 @@ expression: "complete_and_resolve(&server, uri, 1, 18)?"
"character": 18
}
},
- "newText": "gray"
+ "newText": "HTML"
}
},
{
- "label": "rgb",
+ "label": "RGB",
"kind": 1,
"preselect": false,
"sortText": "01",
@@ -41,11 +41,11 @@ expression: "complete_and_resolve(&server, uri, 1, 18)?"
"character": 18
}
},
- "newText": "rgb"
+ "newText": "RGB"
}
},
{
- "label": "RGB",
+ "label": "cmyk",
"kind": 1,
"preselect": false,
"sortText": "02",
@@ -60,11 +60,11 @@ expression: "complete_and_resolve(&server, uri, 1, 18)?"
"character": 18
}
},
- "newText": "RGB"
+ "newText": "cmyk"
}
},
{
- "label": "HTML",
+ "label": "gray",
"kind": 1,
"preselect": false,
"sortText": "03",
@@ -79,11 +79,11 @@ expression: "complete_and_resolve(&server, uri, 1, 18)?"
"character": 18
}
},
- "newText": "HTML"
+ "newText": "gray"
}
},
{
- "label": "cmyk",
+ "label": "rgb",
"kind": 1,
"preselect": false,
"sortText": "04",
@@ -98,7 +98,7 @@ expression: "complete_and_resolve(&server, uri, 1, 18)?"
"character": 18
}
},
- "newText": "cmyk"
+ "newText": "rgb"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap b/support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color_set.snap
index 7e6f4a1de6..dd49043a9e 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__color_model_define_color_set.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 391
expression: "complete_and_resolve(&server, uri, 2, 17)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap b/support/texlab/tests/it/snapshots/it__completion__latex__color_name.snap
index b34b32f5ac..2b428d0e72 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__color_name.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 355
expression: "complete_and_resolve(&server, uri, 0, 9)?"
-
---
{
"isIncomplete": false,
@@ -26,7 +26,7 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
}
},
{
- "label": "RedOrange",
+ "label": "Red",
"kind": 1,
"preselect": false,
"sortText": "01",
@@ -41,11 +41,11 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
"character": 9
}
},
- "newText": "RedOrange"
+ "newText": "Red"
}
},
{
- "label": "Red",
+ "label": "RedOrange",
"kind": 1,
"preselect": false,
"sortText": "02",
@@ -60,7 +60,7 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
"character": 9
}
},
- "newText": "Red"
+ "newText": "RedOrange"
}
},
{
@@ -83,7 +83,7 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
}
},
{
- "label": "OrangeRed",
+ "label": "BrickRed",
"kind": 1,
"preselect": false,
"sortText": "04",
@@ -98,11 +98,11 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
"character": 9
}
},
- "newText": "OrangeRed"
+ "newText": "BrickRed"
}
},
{
- "label": "VioletRed",
+ "label": "OrangeRed",
"kind": 1,
"preselect": false,
"sortText": "05",
@@ -117,11 +117,11 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
"character": 9
}
},
- "newText": "VioletRed"
+ "newText": "OrangeRed"
}
},
{
- "label": "BrickRed",
+ "label": "RubineRed",
"kind": 1,
"preselect": false,
"sortText": "06",
@@ -136,7 +136,7 @@ expression: "complete_and_resolve(&server, uri, 0, 9)?"
"character": 9
}
},
- "newText": "BrickRed"
+ "newText": "RubineRed"
}
}
]
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap b/support/texlab/tests/it/snapshots/it__completion__latex__empty_document.snap
index 7f8638455c..f291fddd8b 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__empty_document.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 194
expression: "complete_and_resolve(&server, uri, 0, 0)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command.snap
index 49f65f06ca..39b89d46d4 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
-expression: "complete_and_resolve(&server, uri, 0, 1)?"
-
+source: tests/it/completion.rs
+assertion_line: 414
+expression: "complete_and_resolve(&server, uri, 0, 2)?"
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_environment.snap
index 8d01ea7b8d..5e0ea8e44d 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_environment.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 460
expression: "complete_and_resolve(&server, uri, 6, 10)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_glyph.snap
index 293b190176..6e7aacacbb 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__kernel_command_glyph.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 437
expression: "complete_and_resolve(&server, uri, 7, 7)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap b/support/texlab/tests/it/snapshots/it__completion__latex__label.snap
index efcaa02ce1..ffdfa17dd7 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__label.snap
@@ -1,18 +1,18 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 699
expression: "complete_and_resolve(&server, uri, 4, 5)?"
-
---
{
"isIncomplete": false,
"items": [
{
- "label": "sec:bar",
+ "label": "eq:bar",
"kind": 1,
- "detail": "Section 2 (Bar)",
+ "detail": "Equation (2)",
"preselect": false,
- "sortText": "00 sec:bar Section 2 (Bar)",
- "filterText": "sec:bar Section 2 (Bar)",
+ "sortText": "00 eq:bar Equation (2)",
+ "filterText": "eq:bar Equation (2)",
"textEdit": {
"range": {
"start": {
@@ -24,16 +24,16 @@ expression: "complete_and_resolve(&server, uri, 4, 5)?"
"character": 5
}
},
- "newText": "sec:bar"
+ "newText": "eq:bar"
}
},
{
- "label": "sec:foo",
+ "label": "eq:foo",
"kind": 1,
- "detail": "Section 1 (Foo)",
+ "detail": "Equation (1)",
"preselect": false,
- "sortText": "01 sec:foo Section 1 (Foo)",
- "filterText": "sec:foo Section 1 (Foo)",
+ "sortText": "01 eq:foo Equation (1)",
+ "filterText": "eq:foo Equation (1)",
"textEdit": {
"range": {
"start": {
@@ -45,16 +45,17 @@ expression: "complete_and_resolve(&server, uri, 4, 5)?"
"character": 5
}
},
- "newText": "sec:foo"
+ "newText": "eq:foo"
}
},
{
- "label": "eq:foo",
+ "label": "fig:baz",
"kind": 1,
- "detail": "Equation (1)",
+ "detail": "Figure 1",
+ "documentation": "Baz",
"preselect": false,
- "sortText": "02 eq:foo Equation (1)",
- "filterText": "eq:foo Equation (1)",
+ "sortText": "02 fig:baz Figure 1: Baz",
+ "filterText": "fig:baz Figure 1: Baz",
"textEdit": {
"range": {
"start": {
@@ -66,16 +67,16 @@ expression: "complete_and_resolve(&server, uri, 4, 5)?"
"character": 5
}
},
- "newText": "eq:foo"
+ "newText": "fig:baz"
}
},
{
- "label": "eq:bar",
+ "label": "sec:bar",
"kind": 1,
- "detail": "Equation (2)",
+ "detail": "Section 2 (Bar)",
"preselect": false,
- "sortText": "03 eq:bar Equation (2)",
- "filterText": "eq:bar Equation (2)",
+ "sortText": "03 sec:bar Section 2 (Bar)",
+ "filterText": "sec:bar Section 2 (Bar)",
"textEdit": {
"range": {
"start": {
@@ -87,17 +88,16 @@ expression: "complete_and_resolve(&server, uri, 4, 5)?"
"character": 5
}
},
- "newText": "eq:bar"
+ "newText": "sec:bar"
}
},
{
- "label": "fig:baz",
+ "label": "sec:foo",
"kind": 1,
- "detail": "Figure 1",
- "documentation": "Baz",
+ "detail": "Section 1 (Foo)",
"preselect": false,
- "sortText": "04 fig:baz Figure 1: Baz",
- "filterText": "fig:baz Figure 1: Baz",
+ "sortText": "04 sec:foo Section 1 (Foo)",
+ "filterText": "sec:foo Section 1 (Foo)",
"textEdit": {
"range": {
"start": {
@@ -109,7 +109,7 @@ expression: "complete_and_resolve(&server, uri, 4, 5)?"
"character": 5
}
},
- "newText": "fig:baz"
+ "newText": "sec:foo"
}
},
{
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__multi_line_key.snap b/support/texlab/tests/it/snapshots/it__completion__latex__multi_line_key.snap
index e22d12571b..a3258523bd 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__multi_line_key.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__multi_line_key.snap
@@ -1,8 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
assertion_line: 814
expression: "complete_and_resolve(&server, uri, 0, 11)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap b/support/texlab/tests/it/snapshots/it__completion__latex__package_command.snap
index 6a260fe387..011038f894 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__package_command.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 529
expression: "complete_and_resolve(&server, uri, 3, 7)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__package_environment.snap
index 7dbe95a81b..048cff4986 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__package_environment.snap
@@ -1,13 +1,13 @@
---
-source: tests/integration/completion.rs
-expression: "complete_and_resolve(&server, uri, 5, 5)?"
-
+source: tests/it/completion.rs
+assertion_line: 552
+expression: "complete_and_resolve(&server, uri, 5, 6)?"
---
{
"isIncomplete": false,
"items": [
{
- "label": "alignedat",
+ "label": "align",
"kind": 1,
"detail": "amsmath.sty",
"preselect": false,
@@ -23,11 +23,11 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
"character": 9
}
},
- "newText": "alignedat"
+ "newText": "align"
}
},
{
- "label": "aligned",
+ "label": "align*",
"kind": 1,
"detail": "amsmath.sty",
"preselect": false,
@@ -43,7 +43,7 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
"character": 9
}
},
- "newText": "aligned"
+ "newText": "align*"
}
},
{
@@ -67,7 +67,7 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
}
},
{
- "label": "align",
+ "label": "alignat*",
"kind": 1,
"detail": "amsmath.sty",
"preselect": false,
@@ -83,11 +83,11 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
"character": 9
}
},
- "newText": "align"
+ "newText": "alignat*"
}
},
{
- "label": "align*",
+ "label": "aligned",
"kind": 1,
"detail": "amsmath.sty",
"preselect": false,
@@ -103,11 +103,11 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
"character": 9
}
},
- "newText": "align*"
+ "newText": "aligned"
}
},
{
- "label": "alignat*",
+ "label": "alignedat",
"kind": 1,
"detail": "amsmath.sty",
"preselect": false,
@@ -123,7 +123,7 @@ expression: "complete_and_resolve(&server, uri, 5, 5)?"
"character": 9
}
},
- "newText": "alignat*"
+ "newText": "alignedat"
}
},
{
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap b/support/texlab/tests/it/snapshots/it__completion__latex__package_import.snap
index 992ea91692..4b45166b80 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__package_import.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 586
expression: "complete_and_resolve(&server, uri, 1, 15)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap b/support/texlab/tests/it/snapshots/it__completion__latex__pgf_library.snap
index da34563c8d..23713c6a9e 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__pgf_library.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 753
expression: "complete_and_resolve(&server, uri, 1, 15)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__preselect_environment.snap
index 791f045476..29d6f5bac6 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__preselect_environment.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 716
expression: "complete_and_resolve(&server, uri, 1, 5)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__theorem_environment.snap
index cc0b336e9f..766d6fe526 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__theorem_environment.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
-expression: "complete_and_resolve(&server, uri, 3, 7)?"
-
+source: tests/it/completion.rs
+assertion_line: 735
+expression: "complete_and_resolve(&server, uri, 3, 8)?"
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap b/support/texlab/tests/it/snapshots/it__completion__latex__user_command.snap
index f2f2eef4a5..09efd12c52 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__user_command.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
+source: tests/it/completion.rs
+assertion_line: 773
expression: "complete_and_resolve(&server, uri, 1, 3)?"
-
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap b/support/texlab/tests/it/snapshots/it__completion__latex__user_environment.snap
index 0f85b3fa56..8e3e866d5f 100644
--- a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap
+++ b/support/texlab/tests/it/snapshots/it__completion__latex__user_environment.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/completion.rs
-expression: "complete_and_resolve(&server, uri, 4, 7)?"
-
+source: tests/it/completion.rs
+assertion_line: 793
+expression: "complete_and_resolve(&server, uri, 4, 8)?"
---
{
"isIncomplete": false,
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap b/support/texlab/tests/it/snapshots/it__document_symbol__enumerate_flat.snap
index 1f042106a2..52fb082eb0 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__enumerate_flat.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 90
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap b/support/texlab/tests/it/snapshots/it__document_symbol__enumerate_nested.snap
index 2584b7ef50..10fc2f79ea 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__enumerate_nested.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 54
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap b/support/texlab/tests/it/snapshots/it__document_symbol__equation_flat.snap
index 1f4b3bfe4d..94a220cb97 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__equation_flat.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 170
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap b/support/texlab/tests/it/snapshots/it__document_symbol__equation_nested.snap
index 9ffb191b89..477a9cb585 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__equation_nested.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 130
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap b/support/texlab/tests/it/snapshots/it__document_symbol__float_flat.snap
index c7465ddcc0..ba3a5babe6 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__float_flat.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 270
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap b/support/texlab/tests/it/snapshots/it__document_symbol__float_nested.snap
index 886ffc560c..99262cf5c3 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__float_nested.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 220
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap b/support/texlab/tests/it/snapshots/it__document_symbol__section_flat.snap
index 2f7ad319a7..1b8fecce0a 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__section_flat.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 342
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap b/support/texlab/tests/it/snapshots/it__document_symbol__section_nested.snap
index d5b5eb4b0c..8443ca4c96 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__section_nested.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 306
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap b/support/texlab/tests/it/snapshots/it__document_symbol__theorem_flat.snap
index 8ce3824bea..46765f2039 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__theorem_flat.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 436
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap b/support/texlab/tests/it/snapshots/it__document_symbol__theorem_nested.snap
index 052f62fec7..2748a2ba0b 100644
--- a/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap
+++ b/support/texlab/tests/it/snapshots/it__document_symbol__theorem_nested.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/document_symbol.rs
+source: tests/it/document_symbol.rs
+assertion_line: 389
expression: server.find_document_symbols(uri)?
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap b/support/texlab/tests/it/snapshots/it__hover__empty_bibtex_document.snap
index e064602aa1..0b36355e00 100644
--- a/support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap
+++ b/support/texlab/tests/it/snapshots/it__hover__empty_bibtex_document.snap
@@ -1,6 +1,6 @@
---
-source: tests/integration/hover.rs
+source: tests/it/hover.rs
+assertion_line: 12
expression: "server.hover(uri, 0, 0)?"
-
---
null
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap b/support/texlab/tests/it/snapshots/it__hover__empty_labtex_document.snap
index e064602aa1..c653b05832 100644
--- a/support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap
+++ b/support/texlab/tests/it/snapshots/it__hover__empty_labtex_document.snap
@@ -1,6 +1,6 @@
---
-source: tests/integration/hover.rs
+source: tests/it/hover.rs
+assertion_line: 21
expression: "server.hover(uri, 0, 0)?"
-
---
null
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap b/support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file.snap
index 758c89cfac..6bc9b28a75 100644
--- a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap
+++ b/support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/hover.rs
+source: tests/it/hover.rs
+assertion_line: 51
expression: "server.hover(uri, 3, 8)?"
-
---
{
"contents": {
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap b/support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file_mumber.snap
index 43737f72a1..b7515e7d43 100644
--- a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap
+++ b/support/texlab/tests/it/snapshots/it__hover__label_theorem_child_file_mumber.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/hover.rs
+source: tests/it/hover.rs
+assertion_line: 87
expression: "server.hover(uri, 3, 8)?"
-
---
{
"contents": {
diff --git a/support/texlab/tests/integration/snapshots/integration__issues__510_completion_with_unmatched_braces.snap b/support/texlab/tests/it/snapshots/it__issues__510_completion_with_unmatched_braces.snap
index e421d6615e..3895881165 100644
--- a/support/texlab/tests/integration/snapshots/integration__issues__510_completion_with_unmatched_braces.snap
+++ b/support/texlab/tests/it/snapshots/it__issues__510_completion_with_unmatched_braces.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/issues.rs
+source: tests/it/issues.rs
+assertion_line: 58
expression: "server.complete(uri, 1, 7)?"
-
---
CompletionList {
is_incomplete: false,
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_bibtex.snap
index bac8d65144..a5db160f9a 100644
--- a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap
+++ b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_bibtex.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/workspace_symbol.rs
+source: tests/it/workspace_symbol.rs
+assertion_line: 117
expression: "run(\"bibtex\")?"
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_figure.snap
index 8842d02f7d..aef75c18a1 100644
--- a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap
+++ b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_figure.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/workspace_symbol.rs
+source: tests/it/workspace_symbol.rs
+assertion_line: 99
expression: "run(\"figure\")?"
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_item.snap
index 3434eaff9c..9f43f48dae 100644
--- a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap
+++ b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_item.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/workspace_symbol.rs
+source: tests/it/workspace_symbol.rs
+assertion_line: 105
expression: "run(\"item\")?"
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_math.snap
index d2b7550efe..5e8e3f9f1e 100644
--- a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap
+++ b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_math.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/workspace_symbol.rs
+source: tests/it/workspace_symbol.rs
+assertion_line: 111
expression: "run(\"math\")?"
-
---
[
{
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_section.snap
index e221d55dcb..c075a080b5 100644
--- a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap
+++ b/support/texlab/tests/it/snapshots/it__workspace_symbol__filter_type_section.snap
@@ -1,7 +1,7 @@
---
-source: tests/integration/workspace_symbol.rs
+source: tests/it/workspace_symbol.rs
+assertion_line: 93
expression: "run(\"section\")?"
-
---
[
{
diff --git a/support/texlab/tests/integration/workspace_symbol.rs b/support/texlab/tests/it/workspace_symbol.rs
index 54a0a8808d..4083b32933 100644
--- a/support/texlab/tests/integration/workspace_symbol.rs
+++ b/support/texlab/tests/it/workspace_symbol.rs
@@ -9,7 +9,7 @@ fn run(query: &str) -> Result<Vec<SymbolInformation>> {
server.initialize(ClientCapabilities::default(), None)?;
let uri = Url::parse("http://www.example.com/main.tex")?;
server.open_memory(
- uri.clone(),
+ uri,
r#"
\documentclass{article}
\usepackage{caption}
diff --git a/support/texlab/texlab.pdf b/support/texlab/texlab.pdf
index 162683cb47..4c9134ec2c 100644
--- a/support/texlab/texlab.pdf
+++ b/support/texlab/texlab.pdf
Binary files differ