summaryrefslogtreecommitdiff
path: root/support/texlab
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab')
-rw-r--r--support/texlab/CHANGELOG.md13
-rw-r--r--support/texlab/Cargo.lock572
-rw-r--r--support/texlab/Cargo.toml3
-rw-r--r--support/texlab/crates/base-db/Cargo.toml5
-rw-r--r--support/texlab/crates/base-db/src/config.rs2
-rw-r--r--support/texlab/crates/base-db/src/data.rs110
-rw-r--r--support/texlab/crates/base-db/src/document.rs2
-rw-r--r--support/texlab/crates/base-db/src/graph.rs7
-rw-r--r--support/texlab/crates/base-db/src/lib.rs18
-rw-r--r--support/texlab/crates/base-db/src/semantics.rs17
-rw-r--r--support/texlab/crates/base-db/src/semantics/bib.rs7
-rw-r--r--support/texlab/crates/base-db/src/semantics/tex.rs3
-rw-r--r--support/texlab/crates/base-db/src/util/queries.rs2
-rw-r--r--support/texlab/crates/base-db/src/workspace.rs9
-rw-r--r--support/texlab/crates/completion-data/Cargo.toml6
-rw-r--r--support/texlab/crates/completion/Cargo.toml33
-rw-r--r--support/texlab/crates/completion/benches/bench_main.rs35
-rw-r--r--support/texlab/crates/completion/src/lib.rs187
-rw-r--r--support/texlab/crates/completion/src/providers.rs27
-rw-r--r--support/texlab/crates/completion/src/providers/argument.rs76
-rw-r--r--support/texlab/crates/completion/src/providers/citations.rs71
-rw-r--r--support/texlab/crates/completion/src/providers/color.rs (renamed from support/texlab/crates/texlab/src/features/completion/color.rs)24
-rw-r--r--support/texlab/crates/completion/src/providers/color_model.rs50
-rw-r--r--support/texlab/crates/completion/src/providers/command.rs134
-rw-r--r--support/texlab/crates/completion/src/providers/entry_type.rs40
-rw-r--r--support/texlab/crates/completion/src/providers/environment.rs135
-rw-r--r--support/texlab/crates/completion/src/providers/field.rs66
-rw-r--r--support/texlab/crates/completion/src/providers/glossary.rs88
-rw-r--r--support/texlab/crates/completion/src/providers/import.rs69
-rw-r--r--support/texlab/crates/completion/src/providers/include.rs (renamed from support/texlab/crates/texlab/src/features/completion/include.rs)85
-rw-r--r--support/texlab/crates/completion/src/providers/label.rs101
-rw-r--r--support/texlab/crates/completion/src/providers/tikz_library.rs (renamed from support/texlab/crates/texlab/src/features/completion/tikz_library.rs)32
-rw-r--r--support/texlab/crates/completion/src/tests.rs2071
-rw-r--r--support/texlab/crates/completion/src/util.rs33
-rw-r--r--support/texlab/crates/completion/src/util/builder.rs43
-rw-r--r--support/texlab/crates/completion/src/util/matchers.rs (renamed from support/texlab/crates/texlab/src/features/completion/matcher.rs)0
-rw-r--r--support/texlab/crates/completion/src/util/patterns.rs75
-rw-r--r--support/texlab/crates/definition/Cargo.toml1
-rw-r--r--support/texlab/crates/definition/src/citation.rs2
-rw-r--r--support/texlab/crates/definition/src/label.rs2
-rw-r--r--support/texlab/crates/definition/src/lib.rs9
-rw-r--r--support/texlab/crates/definition/src/string_ref.rs2
-rw-r--r--support/texlab/crates/definition/src/tests.rs19
-rw-r--r--support/texlab/crates/diagnostics/src/tests.rs2
-rw-r--r--support/texlab/crates/hover/src/entry_type.rs2
-rw-r--r--support/texlab/crates/hover/src/tests.rs2
-rw-r--r--support/texlab/crates/parser/Cargo.toml2
-rw-r--r--support/texlab/crates/parser/src/latex.rs3
-rw-r--r--support/texlab/crates/parser/src/latex/lexer/types.rs9
-rw-r--r--support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_931.txt.snap16
-rw-r--r--support/texlab/crates/parser/src/test_data/latex/issue_931.txt1
-rw-r--r--support/texlab/crates/rename/Cargo.toml19
-rw-r--r--support/texlab/crates/rename/src/command.rs37
-rw-r--r--support/texlab/crates/rename/src/entry.rs49
-rw-r--r--support/texlab/crates/rename/src/label.rs25
-rw-r--r--support/texlab/crates/rename/src/lib.rs44
-rw-r--r--support/texlab/crates/rename/src/tests.rs97
-rw-r--r--support/texlab/crates/symbols/Cargo.toml2
-rw-r--r--support/texlab/crates/symbols/src/document/bib.rs2
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__allowed_patterns.snap31
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__enumerate.snap62
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__equation.snap40
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__float.snap40
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__ignored_patterns.snap19
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__section.snap41
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_amsthm.snap53
-rw-r--r--support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_thmtools.snap53
-rw-r--r--support/texlab/crates/symbols/src/document/tests.rs371
-rw-r--r--support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_bibtex.snap36
-rw-r--r--support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_figure.snap24
-rw-r--r--support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_item.snap60
-rw-r--r--support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_math.snap42
-rw-r--r--support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_section.snap78
-rw-r--r--support/texlab/crates/symbols/src/workspace/tests.rs263
-rw-r--r--support/texlab/crates/test-utils/src/fixture.rs75
-rw-r--r--support/texlab/crates/texlab/Cargo.toml33
-rw-r--r--support/texlab/crates/texlab/benches/bench_main.rs21
-rw-r--r--support/texlab/crates/texlab/src/features/completion.rs460
-rw-r--r--support/texlab/crates/texlab/src/features/completion/acronym_ref.rs31
-rw-r--r--support/texlab/crates/texlab/src/features/completion/argument.rs57
-rw-r--r--support/texlab/crates/texlab/src/features/completion/begin_snippet.rs9
-rw-r--r--support/texlab/crates/texlab/src/features/completion/builder.rs784
-rw-r--r--support/texlab/crates/texlab/src/features/completion/citation.rs54
-rw-r--r--support/texlab/crates/texlab/src/features/completion/color_model.rs40
-rw-r--r--support/texlab/crates/texlab/src/features/completion/component_command.rs24
-rw-r--r--support/texlab/crates/texlab/src/features/completion/component_environment.rs18
-rw-r--r--support/texlab/crates/texlab/src/features/completion/entry_type.rs25
-rw-r--r--support/texlab/crates/texlab/src/features/completion/field.rs34
-rw-r--r--support/texlab/crates/texlab/src/features/completion/glossary_ref.rs37
-rw-r--r--support/texlab/crates/texlab/src/features/completion/import.rs52
-rw-r--r--support/texlab/crates/texlab/src/features/completion/label.rs76
-rw-r--r--support/texlab/crates/texlab/src/features/completion/theorem.rs21
-rw-r--r--support/texlab/crates/texlab/src/features/completion/user_command.rs26
-rw-r--r--support/texlab/crates/texlab/src/features/completion/user_environment.rs26
-rw-r--r--support/texlab/crates/texlab/src/features/highlight.rs14
-rw-r--r--support/texlab/crates/texlab/src/features/highlight/label.rs31
-rw-r--r--support/texlab/crates/texlab/src/features/rename.rs91
-rw-r--r--support/texlab/crates/texlab/src/features/rename/command.rs39
-rw-r--r--support/texlab/crates/texlab/src/features/rename/entry.rs55
-rw-r--r--support/texlab/crates/texlab/src/features/rename/label.rs37
-rw-r--r--support/texlab/crates/texlab/src/server.rs54
-rw-r--r--support/texlab/crates/texlab/src/server/options.rs3
-rw-r--r--support/texlab/crates/texlab/src/util.rs10
-rw-r--r--support/texlab/crates/texlab/src/util/chktex.rs17
-rw-r--r--support/texlab/crates/texlab/src/util/cursor.rs316
-rw-r--r--support/texlab/crates/texlab/tests/lsp/text_document.rs2
-rw-r--r--support/texlab/crates/texlab/tests/lsp/text_document/completion.rs914
-rw-r--r--support/texlab/crates/texlab/tests/lsp/text_document/rename.rs106
-rw-r--r--support/texlab/texlab.14
-rw-r--r--support/texlab/texlab.pdfbin26638 -> 26556 bytes
110 files changed, 5360 insertions, 4047 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md
index 9009b272c7..c14a52e934 100644
--- a/support/texlab/CHANGELOG.md
+++ b/support/texlab/CHANGELOG.md
@@ -5,6 +5,19 @@ 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).
+## [5.10.0] - 2023-09-30
+
+### Added
+
+- Allow passing additional arguments to `ChkTeX` using `texlab.chktex.additionalArgs` ([#927](https://github.com/latex-lsp/texlab/issues/927))
+
+### Fixed
+
+- Fix loading bibliographies from `kpathsea` search path ([#923](https://github.com/latex-lsp/texlab/issues/923))
+- Don't report duplicate results when using goto definition on includes ([#924](https://github.com/latex-lsp/texlab/issues/924))
+- Fix project detection when there exist files with the same name ([#923](https://github.com/latex-lsp/texlab/issues/923))
+- Do not report parse errors with `$` in paths ([#931](https://github.com/latex-lsp/texlab/issues/931))
+
## [5.9.2] - 2023-08-14
### Fixed
diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock
index ad9296d29f..d61fd8706d 100644
--- a/support/texlab/Cargo.lock
+++ b/support/texlab/Cargo.lock
@@ -10,29 +10,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
-version = "0.7.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.0.1"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
+checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [
"memchr",
]
[[package]]
-name = "android-tzdata"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
-
-[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -49,30 +34,29 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.3.2"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
- "is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "1.0.0"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
[[package]]
name = "anstyle-parse"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
@@ -88,9 +72,9 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
-version = "1.0.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
@@ -98,9 +82,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.72"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "assert_unordered"
@@ -134,7 +118,6 @@ dependencies = [
"rowan",
"rustc-hash",
"syntax",
- "text-size",
"url",
]
@@ -166,6 +149,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -176,9 +165,9 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.6.0"
+version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
+checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a"
dependencies = [
"memchr",
"regex-automata",
@@ -187,9 +176,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.12.2"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "cast"
@@ -199,9 +188,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
[[package]]
name = "cfg-if"
@@ -211,11 +203,10 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.26"
+version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
- "android-tzdata",
"num-traits",
]
@@ -265,20 +256,19 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.3.15"
+version = "4.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f644d0dac522c8b05ddc39aaaccc5b136d5dc4ff216610c5641e3be5becf56c"
+checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
dependencies = [
"clap_builder",
"clap_derive",
- "once_cell",
]
[[package]]
name = "clap_builder"
-version = "4.3.15"
+version = "4.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af410122b9778e024f9e0fb35682cc09cc3f85cad5e8d3ba8f47a9702df6e73d"
+checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
dependencies = [
"anstream",
"anstyle",
@@ -288,9 +278,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.3.12"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
+checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck",
"proc-macro2",
@@ -300,9 +290,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
@@ -332,6 +322,26 @@ dependencies = [
]
[[package]]
+name = "completion"
+version = "0.0.0"
+dependencies = [
+ "base-db",
+ "completion-data",
+ "criterion",
+ "distro",
+ "expect-test",
+ "fuzzy-matcher",
+ "itertools 0.11.0",
+ "log",
+ "rayon",
+ "rowan",
+ "rustc-hash",
+ "syntax",
+ "test-utils",
+ "url",
+]
+
+[[package]]
name = "completion-data"
version = "0.0.0"
dependencies = [
@@ -345,9 +355,9 @@ dependencies = [
[[package]]
name = "console"
-version = "0.15.6"
+version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0525278dce688103060006713371cedbad27186c7d913f33d866b498da0f595"
+checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
@@ -363,9 +373,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "cpufeatures"
-version = "0.2.7"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
@@ -438,22 +448,22 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.14"
+version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
- "memoffset",
+ "memoffset 0.9.0",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.15"
+version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
@@ -475,6 +485,7 @@ dependencies = [
"base-db",
"itertools 0.11.0",
"rowan",
+ "rustc-hash",
"syntax",
"test-utils",
]
@@ -525,9 +536,9 @@ dependencies = [
[[package]]
name = "dissimilar"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e"
+checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
[[package]]
name = "distro"
@@ -539,9 +550,9 @@ dependencies = [
[[package]]
name = "either"
-version = "1.8.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
@@ -551,9 +562,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding_rs"
-version = "0.8.32"
+version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
dependencies = [
"cfg-if",
]
@@ -569,9 +580,9 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.1"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
dependencies = [
"errno-dragonfly",
"libc",
@@ -600,12 +611,9 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "1.9.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
-dependencies = [
- "instant",
-]
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
[[package]]
name = "fern"
@@ -618,30 +626,30 @@ dependencies = [
[[package]]
name = "file-id"
-version = "0.1.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13be71e6ca82e91bc0cb862bebaac0b2d1924a5a1d970c822b2f98b63fda8c3"
+checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9"
dependencies = [
- "winapi-util",
+ "windows-sys 0.48.0",
]
[[package]]
name = "filetime"
-version = "0.2.21"
+version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.2.16",
+ "redox_syscall 0.3.5",
"windows-sys 0.48.0",
]
[[package]]
name = "flate2"
-version = "1.0.26"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -703,9 +711,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
@@ -714,11 +722,11 @@ dependencies = [
[[package]]
name = "globset"
-version = "0.4.10"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
+checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d"
dependencies = [
- "aho-corasick 0.7.20",
+ "aho-corasick",
"bstr",
"fnv",
"log",
@@ -745,18 +753,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hover"
@@ -807,7 +806,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"inotify-sys",
"libc",
]
@@ -823,9 +822,9 @@ dependencies = [
[[package]]
name = "insta"
-version = "1.31.0"
+version = "1.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0770b0a3d4c70567f0d58331f3088b0e4c4f56c9b8d764efe654b4a5d46de3a"
+checksum = "a3e02c584f4595792d09509a94cdb92a3cef7592b1eb2d9877ee6f527062d0ea"
dependencies = [
"console",
"globset",
@@ -840,33 +839,12 @@ dependencies = [
]
[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "io-lifetimes"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
-dependencies = [
- "hermit-abi 0.3.1",
- "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
name = "is-terminal"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "hermit-abi 0.3.1",
- "io-lifetimes",
+ "hermit-abi",
"rustix",
"windows-sys 0.48.0",
]
@@ -901,9 +879,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "joinery"
@@ -913,18 +891,18 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5"
[[package]]
name = "js-sys"
-version = "0.3.63"
+version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kqueue"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
+checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
dependencies = [
"kqueue-sys",
"libc",
@@ -932,11 +910,11 @@ dependencies = [
[[package]]
name = "kqueue-sys"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
+checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"libc",
]
@@ -948,9 +926,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.147"
+version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "linked-hash-map"
@@ -960,15 +938,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
-version = "0.3.8"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
[[package]]
name = "lock_api"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
@@ -976,9 +954,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.19"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "logos"
@@ -1014,9 +992,9 @@ dependencies = [
[[package]]
name = "lsp-server"
-version = "0.7.2"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ea9ae5a5082ca3b6ae824fc7666cd206b99168a4d4c769ad8fe9cc740df6a6"
+checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
dependencies = [
"crossbeam-channel",
"log",
@@ -1026,11 +1004,11 @@ dependencies = [
[[package]]
name = "lsp-types"
-version = "0.94.0"
+version = "0.94.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
+checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
@@ -1039,9 +1017,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "memoffset"
@@ -1053,6 +1031,15 @@ dependencies = [
]
[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1063,42 +1050,44 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.6"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"log",
"wasi",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
]
[[package]]
name = "notify"
-version = "6.0.1"
+version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5738a2795d57ea20abec2d6d76c6081186709c0024187cd5977265eda6598b51"
+checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
- "bitflags",
+ "bitflags 2.4.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
+ "log",
"mio",
"walkdir",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
]
[[package]]
name = "notify-debouncer-full"
-version = "0.2.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "416969970ec751a5d702a88c6cd19ac1332abe997fce43f96db0418550426241"
+checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154"
dependencies = [
"crossbeam-channel",
"file-id",
+ "log",
"notify",
"parking_lot",
"walkdir",
@@ -1106,20 +1095,20 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi 0.2.6",
+ "hermit-abi",
"libc",
]
@@ -1153,15 +1142,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.2.16",
+ "redox_syscall 0.3.5",
"smallvec",
- "windows-sys 0.45.0",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1185,19 +1174,20 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pest"
-version = "2.6.0"
+version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70"
+checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33"
dependencies = [
+ "memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
-version = "2.6.0"
+version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb"
+checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a"
dependencies = [
"pest",
"pest_generator",
@@ -1205,9 +1195,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.6.0"
+version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e"
+checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141"
dependencies = [
"pest",
"pest_meta",
@@ -1218,9 +1208,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.6.0"
+version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
+checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f"
dependencies = [
"once_cell",
"pest",
@@ -1229,18 +1219,18 @@ dependencies = [
[[package]]
name = "phf"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
+checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
@@ -1248,9 +1238,9 @@ dependencies = [
[[package]]
name = "phf_generator"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
+checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
@@ -1258,18 +1248,18 @@ dependencies = [
[[package]]
name = "phf_shared"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
+checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "plotters"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
@@ -1280,33 +1270,33 @@ dependencies = [
[[package]]
name = "plotters-backend"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
-version = "0.3.3"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.31"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -1328,9 +1318,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rayon"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
@@ -1338,14 +1328,12 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
]
[[package]]
@@ -1354,7 +1342,7 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -1363,7 +1351,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -1389,25 +1377,25 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.9.1"
+version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
+checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
dependencies = [
- "aho-corasick 1.0.1",
+ "aho-corasick",
"memchr",
"regex-automata",
- "regex-syntax 0.7.4",
+ "regex-syntax 0.7.5",
]
[[package]]
name = "regex-automata"
-version = "0.3.3"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
+checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
dependencies = [
- "aho-corasick 1.0.1",
+ "aho-corasick",
"memchr",
- "regex-syntax 0.7.4",
+ "regex-syntax 0.7.5",
]
[[package]]
@@ -1418,9 +1406,20 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
-version = "0.7.4"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
+
+[[package]]
+name = "rename"
+version = "0.0.0"
+dependencies = [
+ "base-db",
+ "rowan",
+ "rustc-hash",
+ "syntax",
+ "test-utils",
+]
[[package]]
name = "rowan"
@@ -1430,7 +1429,7 @@ checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
dependencies = [
"countme",
"hashbrown",
- "memoffset",
+ "memoffset 0.8.0",
"rustc-hash",
"text-size",
]
@@ -1443,13 +1442,12 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
-version = "0.37.19"
+version = "0.38.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
+checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
dependencies = [
- "bitflags",
+ "bitflags 2.4.0",
"errno",
- "io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
@@ -1457,9 +1455,9 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.13"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
@@ -1472,24 +1470,24 @@ dependencies = [
[[package]]
name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.171"
+version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.171"
+version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
@@ -1498,9 +1496,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.103"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
+checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [
"itoa",
"ryu",
@@ -1519,9 +1517,9 @@ dependencies = [
[[package]]
name = "serde_repr"
-version = "0.1.14"
+version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731"
+checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
@@ -1530,9 +1528,9 @@ dependencies = [
[[package]]
name = "sha2"
-version = "0.10.6"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -1547,9 +1545,9 @@ checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
[[package]]
name = "siphasher"
-version = "0.3.10"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallstr"
@@ -1562,9 +1560,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.10.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "strsim"
@@ -1578,7 +1576,7 @@ version = "0.0.0"
dependencies = [
"base-db",
"distro",
- "insta",
+ "expect-test",
"itertools 0.11.0",
"regex",
"rowan",
@@ -1590,9 +1588,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.26"
+version = "2.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
dependencies = [
"proc-macro2",
"quote",
@@ -1609,11 +1607,10 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.6.0"
+version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
+checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [
- "autocfg",
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
@@ -1633,7 +1630,7 @@ dependencies = [
[[package]]
name = "texlab"
-version = "5.9.2"
+version = "5.10.0"
dependencies = [
"anyhow",
"assert_unordered",
@@ -1641,12 +1638,12 @@ dependencies = [
"citeproc",
"clap",
"commands",
+ "completion",
"completion-data",
"criterion",
"crossbeam-channel",
"definition",
"diagnostics",
- "dirs",
"distro",
"encoding_rs",
"encoding_rs_io",
@@ -1667,6 +1664,7 @@ dependencies = [
"rayon",
"references",
"regex",
+ "rename",
"rowan",
"rustc-hash",
"serde",
@@ -1687,18 +1685,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
[[package]]
name = "thiserror"
-version = "1.0.43"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
+checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.43"
+version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
+checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
@@ -1762,15 +1760,15 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicode-bidi"
@@ -1786,9 +1784,9 @@ checksum = "916a391c62ffbc5b6220fdafa353e93b530d21904d82ca1a2e29cdb5cfb4399f"
[[package]]
name = "unicode-ident"
-version = "1.0.8"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
@@ -1837,9 +1835,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
@@ -1853,9 +1851,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.86"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -1863,9 +1861,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.86"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
@@ -1878,9 +1876,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.86"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1888,9 +1886,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.86"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
@@ -1901,15 +1899,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.86"
+version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "web-sys"
-version = "0.3.63"
+version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -1933,9 +1931,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@@ -1961,7 +1959,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1981,17 +1979,17 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
]
[[package]]
@@ -2002,9 +2000,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
@@ -2014,9 +2012,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
@@ -2026,9 +2024,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
@@ -2038,9 +2036,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
@@ -2050,9 +2048,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -2062,9 +2060,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
@@ -2074,9 +2072,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "yaml-rust"
diff --git a/support/texlab/Cargo.toml b/support/texlab/Cargo.toml
index 59e182bf20..7cb98c8b0e 100644
--- a/support/texlab/Cargo.toml
+++ b/support/texlab/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
members = ["crates/*"]
+resolver = "2"
[workspace.package]
license = "GPL-3.0"
@@ -8,7 +9,7 @@ authors = [
"Patrick Förster <patrick.foerster@outlook.de>",
]
edition = "2021"
-rust-version = "1.65"
+rust-version = "1.70"
[profile.release]
lto = "thin"
diff --git a/support/texlab/crates/base-db/Cargo.toml b/support/texlab/crates/base-db/Cargo.toml
index 0ae198db7b..b440ff5c61 100644
--- a/support/texlab/crates/base-db/Cargo.toml
+++ b/support/texlab/crates/base-db/Cargo.toml
@@ -10,16 +10,15 @@ rust-version.workspace = true
dirs = "5.0.1"
distro = { path = "../distro" }
itertools = "0.11.0"
-log = "0.4.19"
+log = "0.4.20"
notify = "6.0.1"
once_cell = "1.18.0"
parser = { path = "../parser" }
percent-encoding = "2.3.0"
-regex = "1.9.1"
+regex = "1.9.5"
rowan = "0.15.11"
rustc-hash = "1.1.0"
syntax = { path = "../syntax" }
-text-size = "1.1.1"
url = "=2.3.1"
bibtex-utils = { path = "../bibtex-utils" }
diff --git a/support/texlab/crates/base-db/src/config.rs b/support/texlab/crates/base-db/src/config.rs
index bffd28b987..65728e08c2 100644
--- a/support/texlab/crates/base-db/src/config.rs
+++ b/support/texlab/crates/base-db/src/config.rs
@@ -41,6 +41,7 @@ pub struct ChktexConfig {
pub on_open: bool,
pub on_save: bool,
pub on_edit: bool,
+ pub additional_args: Vec<String>,
}
#[derive(Debug)]
@@ -139,6 +140,7 @@ impl Default for ChktexConfig {
on_open: false,
on_save: false,
on_edit: false,
+ additional_args: Vec::new(),
}
}
}
diff --git a/support/texlab/crates/base-db/src/data.rs b/support/texlab/crates/base-db/src/data.rs
index 64313c0ec7..b24c2bb77a 100644
--- a/support/texlab/crates/base-db/src/data.rs
+++ b/support/texlab/crates/base-db/src/data.rs
@@ -1,4 +1,4 @@
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct BibtexEntryType<'a> {
pub name: &'a str,
pub category: BibtexEntryTypeCategory,
@@ -16,7 +16,7 @@ pub enum BibtexEntryTypeCategory {
Collection,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct BibtexFieldType<'a> {
pub name: &'a str,
pub documentation: &'a str,
@@ -37,267 +37,267 @@ impl<'a> BibtexFieldType<'a> {
pub static BIBTEX_ENTRY_TYPES: &[BibtexEntryType<'static>] = &[
BibtexEntryType {
- name: "preamble",
+ name: "@preamble",
category: BibtexEntryTypeCategory::Misc,
documentation: None,
},
BibtexEntryType {
- name: "string",
+ name: "@string",
category: BibtexEntryTypeCategory::String,
documentation: None,
},
BibtexEntryType {
- name: "comment",
+ name: "@comment",
category: BibtexEntryTypeCategory::Misc,
documentation: None,
},
BibtexEntryType {
- name: "article",
+ name: "@article",
category: BibtexEntryTypeCategory::Article,
documentation: Some("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`"),
},
BibtexEntryType {
- name: "book",
+ name: "@book",
category: BibtexEntryTypeCategory::Book,
documentation: Some("A single-volume book with one or more authors where the authors share credit for\n the work as a whole. This entry type also covers the function of the `@inbook` type\n of traditional BibTeX.\n\nRequired fields: `author`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "mvbook",
+ name: "@mvbook",
category: BibtexEntryTypeCategory::Book,
documentation: Some("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`"),
},
BibtexEntryType {
- name: "inbook",
+ name: "@inbook",
category: BibtexEntryTypeCategory::Part,
documentation: Some("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`"),
},
BibtexEntryType {
- name: "bookinbook",
+ name: "@bookinbook",
category: BibtexEntryTypeCategory::Part,
documentation: Some("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."),
},
BibtexEntryType {
- name: "suppbook",
+ name: "@suppbook",
category: BibtexEntryTypeCategory::Book,
documentation: Some("Supplemental material in a `@book`. This type is closely related to the `@inbook`\n entry type. While `@inbook` is primarily intended for a part of a book with its own\n title (e. g., a single essay in a collection of essays by the same author), this type is\n provided for elements such as prefaces, introductions, forewords, afterwords, etc.\n which often have a generic title only. Style guides may require such items to be\n formatted differently from other `@inbook` items. The standard styles will treat this\n entry type as an alias for `@inbook`."),
},
BibtexEntryType {
- name: "booklet",
+ name: "@booklet",
category: BibtexEntryTypeCategory::Book,
documentation: Some("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`"),
},
BibtexEntryType {
- name: "collection",
+ name: "@collection",
category: BibtexEntryTypeCategory::Collection,
documentation: Some("A single-volume collection with multiple, self-contained contributions by distinct\n authors which have their own title. The work as a whole has no overall author but it\n will usually have an editor.\n\nRequired fields: `editor`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "mvcollection",
+ name: "@mvcollection",
category: BibtexEntryTypeCategory::Collection,
documentation: Some("A multi-volume `@collection`. For backwards compatibility, multi-volume collections\n are also supported by the entry type `@collection`. However, it is advisable\n to make use of the dedicated entry type `@mvcollection`.\n\nRequired fields: `editor`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "incollection",
+ name: "@incollection",
category: BibtexEntryTypeCategory::Part,
documentation: Some("A contribution to a collection which forms a self-contained unit with a distinct author\n and title. The `author` refers to the `title`, the `editor` to the `booktitle`, i. e.,\n the title of the collection.\n\nRequired fields: `author`, `title`, `booktitle`, `year/date`"),
},
BibtexEntryType {
- name: "suppcollection",
+ name: "@suppcollection",
category: BibtexEntryTypeCategory::Collection,
documentation: Some("Supplemental material in a `@collection`. This type is similar to `@suppbook` but\n related to the `@collection` entry type. The standard styles will treat this entry\n type as an alias for `@incollection`."),
},
BibtexEntryType {
- name: "manual",
+ name: "@manual",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Technical or other documentation, not necessarily in printed form. The author or\n editor is omissible.\n\nRequired fields: `author/editor`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "misc",
+ name: "@misc",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("A fallback type for entries which do not fit into any other category. Use the field\n howpublished to supply publishing information in free format, if applicable. The\n field type may be useful as well. author, editor, and year are omissible.\n\nRequired fields: `author/editor`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "online",
+ name: "@online",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("An online resource. `author`, `editor`, and `year` are omissible.\n This entry type is intended for sources such as web sites which are intrinsically\n online resources. Note that all entry types support the url field. For example, when\n adding an article from an online journal, it may be preferable to use the `@article`\n type and its url field.\n\nRequired fields: `author/editor`, `title`, `year/date`, `url`"),
},
BibtexEntryType {
- name: "patent",
+ name: "@patent",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("A patent or patent request. The number or record token is given in the number\n field. Use the type field to specify the type and the location field to indicate the\n scope of the patent, if different from the scope implied by the type. Note that the\n location field is treated as a key list with this entry type.\n\nRequired fields: `author`, `title`, `number`, `year/date`"),
},
BibtexEntryType {
- name: "periodical",
+ name: "@periodical",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("An complete issue of a periodical, such as a special issue of a journal. The title of\n the periodical is given in the title field. If the issue has its own title in addition to\n the main title of the periodical, it goes in the issuetitle field. The editor is\n omissible.\n\nRequired fields: `editor`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "suppperiodical",
+ name: "@suppperiodical",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Supplemental material in a `@periodical`. This type is similar to `@suppbook`\n but related to the `@periodical` entry type. The role of this entry type may be\n more obvious if you bear in mind that the `@article` type could also be called\n `@inperiodical`. This type may be useful when referring to items such as regular\n columns, obituaries, letters to the editor, etc. which only have a generic title. Style\n guides may require such items to be formatted differently from articles in the strict\n sense of the word. The standard styles will treat this entry type as an alias for\n `@article`."),
},
BibtexEntryType {
- name: "proceedings",
+ name: "@proceedings",
category: BibtexEntryTypeCategory::Book,
documentation: Some("A single-volume conference proceedings. This type is very similar to `@collection`.\n It supports an optional organization field which holds the sponsoring institution.\n The editor is omissible.\n\nRequired fields: `title`, `year/date`"),
},
BibtexEntryType {
- name: "mvproceedings",
+ name: "@mvproceedings",
category: BibtexEntryTypeCategory::Book,
documentation: Some("A multi-volume `@proceedings` entry. For backwards compatibility, multi-volume\n proceedings are also supported by the entry type `@proceedings`. However, it is\n advisable to make use of the dedicated entry type `@mvproceedings`\n\nRequired fields: `title`, `year/date`"),
},
BibtexEntryType {
- name: "inproceedings",
+ name: "@inproceedings",
category: BibtexEntryTypeCategory::Part,
documentation: Some("An article in a conference proceedings. This type is similar to `@incollection`. It\n supports an optional `organization` field.\n\nRequired fields: `author`, `title`, `booktitle`, `year/date`"),
},
BibtexEntryType {
- name: "reference",
+ name: "@reference",
category: BibtexEntryTypeCategory::Collection,
documentation: Some("A single-volume work of reference such as an encyclopedia or a dictionary. This is a\n more specific variant of the generic `@collection` entry type. The standard styles\n will treat this entry type as an alias for `@collection`."),
},
BibtexEntryType {
- name: "mvreference",
+ name: "@mvreference",
category: BibtexEntryTypeCategory::Collection,
documentation: Some("A multi-volume `@reference` entry. The standard styles will treat this entry type\n as an alias for `@mvcollection`. For backwards compatibility, multi-volume references\n are also supported by the entry type `@reference`. However, it is advisable\n to make use of the dedicated entry type `@mvreference`."),
},
BibtexEntryType {
- name: "inreference",
+ name: "@inreference",
category: BibtexEntryTypeCategory::Part,
documentation: Some("An article in a work of reference. This is a more specific variant of the generic\n `@incollection` entry type. The standard styles will treat this entry type as an\n alias for `@incollection`."),
},
BibtexEntryType {
- name: "report",
+ name: "@report",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("A technical report, research report, or white paper published by a university or some\n other institution. Use the `type` field to specify the type of report. The sponsoring\n institution goes in the `institution` field.\n\nRequired fields: `author`, `title`, `type`, `institution`, `year/date`"),
},
BibtexEntryType {
- name: "set",
+ name: "@set",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("An entry set. This entry type is special."),
},
BibtexEntryType {
- name: "thesis",
+ name: "@thesis",
category: BibtexEntryTypeCategory::Thesis,
documentation: Some("A thesis written for an educational institution to satisfy the requirements for a degree.\n Use the `type` field to specify the type of thesis.\n\nRequired fields: `author`, `title`, `type`, `institution`, `year/date`"),
},
BibtexEntryType {
- name: "unpublished",
+ name: "@unpublished",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("A work with an author and a title which has not been formally published, such as\n a manuscript or the script of a talk. Use the fields `howpublished` and `note` to\n supply additional information in free format, if applicable.\n\nRequired fields: `author`, `title`, `year/date`"),
},
BibtexEntryType {
- name: "xdata",
+ name: "@xdata",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("This entry type is special. `@xdata` entries hold data which may be inherited by other\n entries using the `xdata` field. Entries of this type only serve as data containers;\n they may not be cited or added to the bibliography."),
},
BibtexEntryType {
- name: "conference",
+ name: "@conference",
category: BibtexEntryTypeCategory::Part,
documentation: Some("A legacy alias for `@inproceedings`."),
},
BibtexEntryType {
- name: "electronic",
+ name: "@electronic",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("An alias for `@online`."),
},
BibtexEntryType {
- name: "mastersthesis",
+ name: "@mastersthesis",
category: BibtexEntryTypeCategory::Thesis,
documentation: Some("Similar to `@thesis` except that the `type` field is optional and defaults to the\n localised term ‘Master’s thesis’. You may still use the `type` field to override that."),
},
BibtexEntryType {
- name: "phdthesis",
+ name: "@phdthesis",
category: BibtexEntryTypeCategory::Thesis,
documentation: Some("Similar to `@thesis` except that the `type` field is optional and defaults to the\n localised term ‘PhD thesis’. You may still use the `type` field to override that."),
},
BibtexEntryType {
- name: "techreport",
+ name: "@techreport",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Similar to `@report` except that the `type` field is optional and defaults to the\n localised term ‘technical report’. You may still use the `type` field to override that."),
},
BibtexEntryType {
- name: "www",
+ name: "@www",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("An alias for `@online`, provided for `jurabib` compatibility."),
},
BibtexEntryType {
- name: "artwork",
+ name: "@artwork",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Works of the visual arts such as paintings, sculpture, and installations."),
},
BibtexEntryType {
- name: "audio",
+ name: "@audio",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Audio recordings, typically on audio cd, dvd, audio cassette, or similar media. See\n also `@music`."),
},
BibtexEntryType {
- name: "bibnote",
+ name: "@bibnote",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("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."),
},
BibtexEntryType {
- name: "commentary",
+ name: "@commentary",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Commentaries which have a status different from regular books, such as legal commentaries."),
},
BibtexEntryType {
- name: "image",
+ name: "@image",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Images, pictures, photographs, and similar media."),
},
BibtexEntryType {
- name: "jurisdiction",
+ name: "@jurisdiction",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Court decisions, court recordings, and similar things."),
},
BibtexEntryType {
- name: "legislation",
+ name: "@legislation",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Laws, bills, legislative proposals, and similar things."),
},
BibtexEntryType {
- name: "legal",
+ name: "@legal",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Legal documents such as treaties."),
},
BibtexEntryType {
- name: "letter",
+ name: "@letter",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Personal correspondence such as letters, emails, memoranda, etc."),
},
BibtexEntryType {
- name: "movie",
+ name: "@movie",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Motion pictures. See also `@video`."),
},
BibtexEntryType {
- name: "music",
+ name: "@music",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Musical recordings. This is a more specific variant of `@audio`."),
},
BibtexEntryType {
- name: "performance",
+ name: "@performance",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Musical and theatrical performances as well as other works of the performing arts.\n This type refers to the event as opposed to a recording, a score, or a printed play."),
},
BibtexEntryType {
- name: "review",
+ name: "@review",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Reviews of some other work. This is a more specific variant of the `@article` type.\n The standard styles will treat this entry type as an alias for `@article`."),
},
BibtexEntryType {
- name: "software",
+ name: "@software",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Computer software."),
},
BibtexEntryType {
- name: "standard",
+ name: "@standard",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("National and international standards issued by a standards body such as the International\n Organization for Standardization."),
},
BibtexEntryType {
- name: "video",
+ name: "@video",
category: BibtexEntryTypeCategory::Misc,
documentation: Some("Audiovisual recordings, typically on dvd, vhs cassette, or similar media. See also\n `@movie`."),
}
diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs
index 4b84adf7de..8642694811 100644
--- a/support/texlab/crates/base-db/src/document.rs
+++ b/support/texlab/crates/base-db/src/document.rs
@@ -1,8 +1,8 @@
use std::path::PathBuf;
use distro::Language;
+use rowan::TextRange;
use syntax::{bibtex, latex, BuildError};
-use text_size::TextRange;
use url::Url;
use crate::{
diff --git a/support/texlab/crates/base-db/src/graph.rs b/support/texlab/crates/base-db/src/graph.rs
index 2dd732a449..46b9038de8 100644
--- a/support/texlab/crates/base-db/src/graph.rs
+++ b/support/texlab/crates/base-db/src/graph.rs
@@ -96,7 +96,10 @@ impl<'a> Graph<'a> {
let distro_files = file_names
.iter()
.filter_map(|name| file_name_db.get(name))
- .filter(|path| home_dir.map_or(false, |dir| path.starts_with(dir)))
+ .filter(|path| {
+ home_dir.map_or(false, |dir| path.starts_with(dir))
+ || Language::from_path(path) == Some(Language::Bib)
+ })
.flat_map(Url::from_file_path);
for target_uri in file_names
@@ -123,6 +126,8 @@ impl<'a> Graph<'a> {
target,
weight,
});
+
+ break;
}
None => {
self.missing.push(target_uri);
diff --git a/support/texlab/crates/base-db/src/lib.rs b/support/texlab/crates/base-db/src/lib.rs
index 3ad5f74836..d2db7d4391 100644
--- a/support/texlab/crates/base-db/src/lib.rs
+++ b/support/texlab/crates/base-db/src/lib.rs
@@ -7,3 +7,21 @@ pub mod util;
mod workspace;
pub use self::{config::*, document::*, workspace::*};
+
+#[derive(Debug)]
+pub struct FeatureParams<'a> {
+ pub document: &'a Document,
+ pub project: Project<'a>,
+ pub workspace: &'a Workspace,
+}
+
+impl<'a> FeatureParams<'a> {
+ pub fn new(workspace: &'a Workspace, document: &'a Document) -> Self {
+ let project = workspace.project(document);
+ Self {
+ document,
+ project,
+ workspace,
+ }
+ }
+}
diff --git a/support/texlab/crates/base-db/src/semantics.rs b/support/texlab/crates/base-db/src/semantics.rs
index d05c9ffb87..f83d758cf7 100644
--- a/support/texlab/crates/base-db/src/semantics.rs
+++ b/support/texlab/crates/base-db/src/semantics.rs
@@ -8,6 +8,19 @@ pub struct Span {
pub range: rowan::TextRange,
}
+impl Span {
+ pub fn new(text: String, range: rowan::TextRange) -> Self {
+ Self { text, range }
+ }
+
+ pub fn empty(offset: rowan::TextSize) -> Self {
+ Self {
+ text: String::new(),
+ range: rowan::TextRange::empty(offset),
+ }
+ }
+}
+
impl std::fmt::Debug for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Span")
@@ -26,8 +39,8 @@ impl From<&syntax::latex::Key> for Span {
}
}
-impl From<&syntax::bibtex::SyntaxToken> for Span {
- fn from(token: &syntax::bibtex::SyntaxToken) -> Self {
+impl<L: rowan::Language> From<&rowan::SyntaxToken<L>> for Span {
+ fn from(token: &rowan::SyntaxToken<L>) -> Self {
Self {
text: token.text().into(),
range: token.text_range(),
diff --git a/support/texlab/crates/base-db/src/semantics/bib.rs b/support/texlab/crates/base-db/src/semantics/bib.rs
index 29f9477896..14fa98fb6e 100644
--- a/support/texlab/crates/base-db/src/semantics/bib.rs
+++ b/support/texlab/crates/base-db/src/semantics/bib.rs
@@ -1,8 +1,7 @@
use bibtex_utils::field::text::TextFieldData;
use itertools::Itertools;
-use rowan::ast::AstNode;
+use rowan::{ast::AstNode, TextRange};
use syntax::bibtex::{self, HasName, HasType, HasValue};
-use text_size::TextRange;
use crate::data::{BibtexEntryType, BibtexEntryTypeCategory};
@@ -28,7 +27,7 @@ impl Semantics {
fn process_entry(&mut self, entry: bibtex::Entry) {
if let Some(name) = entry.name_token() {
let type_token = entry.type_token().unwrap();
- let category = BibtexEntryType::find(&type_token.text()[1..])
+ let category = BibtexEntryType::find(type_token.text())
.map_or(BibtexEntryTypeCategory::Misc, |ty| ty.category);
let field_values = entry
@@ -65,7 +64,7 @@ impl Semantics {
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Entry {
pub name: Span,
pub full_range: TextRange,
diff --git a/support/texlab/crates/base-db/src/semantics/tex.rs b/support/texlab/crates/base-db/src/semantics/tex.rs
index 9954098b36..c1e9f45bb9 100644
--- a/support/texlab/crates/base-db/src/semantics/tex.rs
+++ b/support/texlab/crates/base-db/src/semantics/tex.rs
@@ -1,7 +1,6 @@
-use rowan::{ast::AstNode, TextLen};
+use rowan::{ast::AstNode, TextLen, TextRange};
use rustc_hash::FxHashSet;
use syntax::latex::{self, HasBrack, HasCurly};
-use text_size::TextRange;
use super::Span;
diff --git a/support/texlab/crates/base-db/src/util/queries.rs b/support/texlab/crates/base-db/src/util/queries.rs
index 9d4418c328..8c20589172 100644
--- a/support/texlab/crates/base-db/src/util/queries.rs
+++ b/support/texlab/crates/base-db/src/util/queries.rs
@@ -1,6 +1,6 @@
use itertools::Itertools;
+use rowan::{TextRange, TextSize};
use rustc_hash::FxHashMap;
-use text_size::{TextRange, TextSize};
use url::Url;
use crate::{
diff --git a/support/texlab/crates/base-db/src/workspace.rs b/support/texlab/crates/base-db/src/workspace.rs
index 35e3904d75..b4a524ad4a 100644
--- a/support/texlab/crates/base-db/src/workspace.rs
+++ b/support/texlab/crates/base-db/src/workspace.rs
@@ -5,9 +5,8 @@ use std::{
use distro::{Distro, Language};
use itertools::Itertools;
-use rowan::TextRange;
+use rowan::{TextLen, TextRange};
use rustc_hash::FxHashSet;
-use text_size::TextLen;
use url::Url;
use crate::{graph, util::LineCol, Config, Document, DocumentData, DocumentParams, Owner};
@@ -267,6 +266,7 @@ impl Workspace {
fn discover_parents(&mut self, checked_paths: &mut FxHashSet<PathBuf>) -> bool {
let dirs = self
.iter()
+ .filter(|document| document.language != Language::Bib)
.filter_map(|document| document.path.as_deref())
.flat_map(|path| path.ancestors().skip(1))
.filter(|path| self.contains(path))
@@ -297,11 +297,12 @@ impl Workspace {
let Some(lang) = Language::from_path(&file) else {
continue;
};
+
if !matches!(lang, Language::Tex | Language::Root | Language::Tectonic) {
continue;
}
- if self.lookup_path(&file).is_none() {
+ if self.lookup_path(&file).is_none() && file.exists() {
changed |= self.load(&file, lang, Owner::Server).is_ok();
checked_paths.insert(file);
}
@@ -323,7 +324,7 @@ impl Workspace {
let mut changed = false;
for file in files {
let language = Language::from_path(&file).unwrap_or(Language::Tex);
- if self.lookup_path(&file).is_none() {
+ if self.lookup_path(&file).is_none() && file.exists() {
changed |= self.load(&file, language, Owner::Server).is_ok();
checked_paths.insert(file);
}
diff --git a/support/texlab/crates/completion-data/Cargo.toml b/support/texlab/crates/completion-data/Cargo.toml
index 16783ce87e..b0053c7701 100644
--- a/support/texlab/crates/completion-data/Cargo.toml
+++ b/support/texlab/crates/completion-data/Cargo.toml
@@ -7,12 +7,12 @@ edition.workspace = true
rust-version.workspace = true
[dependencies]
-flate2 = "1.0.26"
+flate2 = "1.0.27"
itertools = "0.11.0"
once_cell = "1.18.0"
rustc-hash = "1.1.0"
-serde = { version = "1.0.171", features = ["derive"] }
-serde_json = "1.0.103"
+serde = { version = "1.0.188", features = ["derive"] }
+serde_json = "1.0.107"
[lib]
doctest = false
diff --git a/support/texlab/crates/completion/Cargo.toml b/support/texlab/crates/completion/Cargo.toml
new file mode 100644
index 0000000000..671728aea8
--- /dev/null
+++ b/support/texlab/crates/completion/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "completion"
+version = "0.0.0"
+license.workspace = true
+authors.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+base-db = { path = "../base-db" }
+completion-data = { path = "../completion-data" }
+fuzzy-matcher = { version = "0.3.7", features = ["compact"] }
+itertools = "0.11.0"
+log = "0.4.20"
+rayon = "1.7.0"
+rowan = "0.15.11"
+rustc-hash = "1.1.0"
+syntax = { path = "../syntax" }
+url = "=2.3.1"
+
+[dev-dependencies]
+criterion = "0.5.1"
+distro = { path = "../distro" }
+test-utils = { path = "../test-utils" }
+expect-test = "1.4.1"
+
+[lib]
+doctest = false
+
+[[bench]]
+name = "bench_main"
+path = "benches/bench_main.rs"
+harness = false
diff --git a/support/texlab/crates/completion/benches/bench_main.rs b/support/texlab/crates/completion/benches/bench_main.rs
new file mode 100644
index 0000000000..66c7a3a726
--- /dev/null
+++ b/support/texlab/crates/completion/benches/bench_main.rs
@@ -0,0 +1,35 @@
+use base_db::{util::LineCol, FeatureParams, Owner, Workspace};
+use completion::CompletionParams;
+use criterion::{criterion_group, criterion_main, Criterion};
+use distro::Language;
+use rowan::TextSize;
+use url::Url;
+
+const CODE: &str = include_str!("../../../texlab.tex");
+
+fn criterion_benchmark(c: &mut Criterion) {
+ c.bench_function("Command", |b| {
+ let uri = Url::parse("http://example.com/texlab.tex").unwrap();
+ let text = CODE.to_string();
+ let mut workspace = Workspace::default();
+ workspace.open(
+ uri.clone(),
+ text,
+ Language::Tex,
+ Owner::Client,
+ LineCol { line: 0, col: 0 },
+ );
+
+ let feature = FeatureParams::new(&workspace, workspace.lookup(&uri).unwrap());
+ let params = CompletionParams {
+ feature,
+ offset: TextSize::from(1),
+ };
+
+ b.iter(|| completion::complete(&params));
+ });
+}
+
+criterion_group!(benches, criterion_benchmark);
+
+criterion_main!(benches);
diff --git a/support/texlab/crates/completion/src/lib.rs b/support/texlab/crates/completion/src/lib.rs
new file mode 100644
index 0000000000..6acec28d49
--- /dev/null
+++ b/support/texlab/crates/completion/src/lib.rs
@@ -0,0 +1,187 @@
+mod providers;
+mod util;
+
+use base_db::{
+ data::{BibtexEntryType, BibtexFieldType},
+ semantics::bib,
+ util::RenderedObject,
+ Document, FeatureParams,
+};
+use rowan::{TextRange, TextSize};
+use util::CompletionBuilder;
+
+pub const LIMIT: usize = 50;
+
+#[derive(Debug)]
+pub struct CompletionParams<'a> {
+ pub feature: FeatureParams<'a>,
+ pub offset: TextSize,
+}
+
+#[derive(Debug, Default)]
+pub struct CompletionResult<'a> {
+ pub items: Vec<CompletionItem<'a>>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct CompletionItem<'a> {
+ pub score: i32,
+ pub range: TextRange,
+ pub preselect: bool,
+ pub data: CompletionItemData<'a>,
+}
+
+impl<'a> CompletionItem<'a> {
+ pub fn new_simple(score: i32, range: TextRange, data: CompletionItemData<'a>) -> Self {
+ Self {
+ score,
+ range,
+ preselect: false,
+ data,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum CompletionItemData<'a> {
+ Command(CommandData<'a>),
+ BeginEnvironment,
+ Citation(CitationData<'a>),
+ Environment(EnvironmentData<'a>),
+ GlossaryEntry(GlossaryEntryData),
+ Label(LabelData<'a>),
+ Color(&'a str),
+ ColorModel(&'a str),
+ File(String),
+ Directory(String),
+ Argument(ArgumentData<'a>),
+ Package(&'a str),
+ DocumentClass(&'a str),
+ EntryType(EntryTypeData<'a>),
+ Field(FieldTypeData<'a>),
+ TikzLibrary(&'a str),
+}
+
+impl<'a> CompletionItemData<'a> {
+ pub fn label<'b: 'a>(&'b self) -> &'a str {
+ match self {
+ Self::Command(data) => data.name,
+ Self::BeginEnvironment => "begin",
+ Self::Citation(data) => &data.entry.name.text,
+ Self::Environment(data) => data.name,
+ Self::GlossaryEntry(data) => &data.name,
+ Self::Label(data) => data.name,
+ Self::Color(name) => name,
+ Self::ColorModel(name) => name,
+ Self::File(name) => &name,
+ Self::Directory(name) => &name,
+ Self::Argument(data) => &data.0.name,
+ Self::Package(name) => name,
+ Self::DocumentClass(name) => name,
+ Self::EntryType(data) => data.0.name,
+ Self::Field(data) => data.0.name,
+ Self::TikzLibrary(name) => name,
+ }
+ }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct CommandData<'a> {
+ pub name: &'a str,
+ pub glyph: Option<&'a str>,
+ pub image: Option<&'a str>,
+ pub package: Option<&'a completion_data::Package<'a>>,
+}
+
+impl<'a> std::fmt::Debug for CommandData<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CommandData")
+ .field("name", &self.name)
+ .field("package", self.package.map_or(&"<user>", |p| &p.file_names))
+ .finish()
+ }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct EnvironmentData<'a> {
+ pub name: &'a str,
+ pub package: Option<&'a completion_data::Package<'a>>,
+}
+
+impl<'a> std::fmt::Debug for EnvironmentData<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("EnvironmentData")
+ .field("name", &self.name)
+ .field("package", self.package.map_or(&"<user>", |p| &p.file_names))
+ .finish()
+ }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct ArgumentData<'a>(pub &'a completion_data::Argument<'a>);
+
+impl<'a> std::fmt::Debug for ArgumentData<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("ArgumentData").field(&self.0.name).finish()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct CitationData<'a> {
+ pub document: &'a Document,
+ pub entry: &'a bib::Entry,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct GlossaryEntryData {
+ pub name: String,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct LabelData<'a> {
+ pub name: &'a str,
+ pub header: Option<String>,
+ pub footer: Option<&'a str>,
+ pub object: Option<RenderedObject<'a>>,
+ pub keywords: String,
+}
+
+#[derive(PartialEq, Eq)]
+pub struct EntryTypeData<'a>(pub BibtexEntryType<'a>);
+
+impl<'a> std::fmt::Debug for EntryTypeData<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("EntryTypeData").field(&self.0.name).finish()
+ }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct FieldTypeData<'a>(pub BibtexFieldType<'a>);
+
+impl<'a> std::fmt::Debug for FieldTypeData<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("FieldTypeData").field(&self.0.name).finish()
+ }
+}
+
+pub fn complete<'a>(params: &'a CompletionParams<'a>) -> CompletionResult<'a> {
+ let mut builder = CompletionBuilder::from(params.feature.workspace);
+ providers::complete_commands(params, &mut builder);
+ providers::complete_environments(params, &mut builder);
+ providers::complete_citations(params, &mut builder);
+ providers::complete_acronyms(params, &mut builder);
+ providers::complete_glossaries(params, &mut builder);
+ providers::complete_labels(params, &mut builder);
+ providers::complete_colors(params, &mut builder);
+ providers::complete_color_models(params, &mut builder);
+ providers::complete_includes(params, &mut builder);
+ providers::complete_arguments(params, &mut builder);
+ providers::complete_imports(params, &mut builder);
+ providers::complete_entry_types(params, &mut builder);
+ providers::complete_fields(params, &mut builder);
+ providers::complete_tikz_libraries(params, &mut builder);
+ builder.finish()
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/support/texlab/crates/completion/src/providers.rs b/support/texlab/crates/completion/src/providers.rs
new file mode 100644
index 0000000000..5160c85d85
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers.rs
@@ -0,0 +1,27 @@
+mod argument;
+mod citations;
+mod color;
+mod color_model;
+mod command;
+mod entry_type;
+mod environment;
+mod field;
+mod glossary;
+mod import;
+mod include;
+mod label;
+mod tikz_library;
+
+pub use argument::complete_arguments;
+pub use citations::complete_citations;
+pub use color::complete_colors;
+pub use color_model::complete_color_models;
+pub use command::complete_commands;
+pub use entry_type::complete_entry_types;
+pub use environment::complete_environments;
+pub use field::complete_fields;
+pub use glossary::{complete_acronyms, complete_glossaries};
+pub use import::complete_imports;
+pub use include::complete_includes;
+pub use label::complete_labels;
+pub use tikz_library::complete_tikz_libraries;
diff --git a/support/texlab/crates/completion/src/providers/argument.rs b/support/texlab/crates/completion/src/providers/argument.rs
new file mode 100644
index 0000000000..6637cecdd6
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/argument.rs
@@ -0,0 +1,76 @@
+use base_db::semantics::Span;
+use rowan::{ast::AstNode, TokenAtOffset};
+use syntax::latex;
+
+use crate::{
+ util::{included_packages, is_inside_latex_curly, CompletionBuilder},
+ ArgumentData, CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_arguments<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let (cursor, group) = find_argument(params)?;
+
+ let command = latex::GenericCommand::cast(group.syntax().parent()?)?;
+
+ let index = command
+ .syntax()
+ .children()
+ .filter_map(latex::CurlyGroup::cast)
+ .position(|g| g.syntax().text_range() == group.syntax().text_range())?;
+
+ let command_name = command.name()?;
+ let command_name = &command_name.text()[1..];
+
+ for package in included_packages(&params.feature) {
+ for package_command in package
+ .commands
+ .iter()
+ .filter(|command| command.name == command_name)
+ {
+ for (_, param) in package_command
+ .parameters
+ .iter()
+ .enumerate()
+ .filter(|(i, _)| *i == index)
+ {
+ for arg in &param.0 {
+ if let Some(score) = builder.matcher.score(&arg.name, &cursor.text) {
+ let data = CompletionItemData::Argument(ArgumentData(arg));
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+ }
+ }
+ }
+
+ Some(())
+}
+
+fn find_argument(params: &CompletionParams) -> Option<(Span, latex::CurlyGroup)> {
+ let data = params.feature.document.data.as_tex()?;
+ let tokens = data.root_node().token_at_offset(params.offset);
+
+ let (span, token) = match tokens.clone().find(|token| token.kind() == latex::WORD) {
+ Some(token) => (Span::from(&token), token),
+ None if matches!(tokens, TokenAtOffset::Between(_, _)) => {
+ (Span::empty(params.offset), tokens.left_biased()?)
+ }
+ None => return None,
+ };
+
+ let group = latex::CurlyGroup::cast(token.parent()?)
+ .or_else(|| {
+ token
+ .parent()
+ .and_then(|node| node.parent())
+ .and_then(latex::CurlyGroup::cast)
+ })
+ .filter(|group| is_inside_latex_curly(group, params.offset))?;
+
+ Some((span, group))
+}
diff --git a/support/texlab/crates/completion/src/providers/citations.rs b/support/texlab/crates/completion/src/providers/citations.rs
new file mode 100644
index 0000000000..631a899203
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/citations.rs
@@ -0,0 +1,71 @@
+use base_db::semantics::Span;
+use rayon::prelude::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
+use rowan::ast::AstNode;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word_list, CompletionBuilder},
+ CitationData, CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_citations<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_citation(params)?;
+
+ for document in &params.feature.project.documents {
+ if let Some(data) = document.data.as_bib() {
+ let items = data.semantics.entries.par_iter().filter_map(|entry| {
+ let score = builder.matcher.score(&entry.name.text, &cursor.text)?;
+ let data = CompletionItemData::Citation(CitationData { document, entry });
+ Some(CompletionItem::new_simple(score, cursor.range, data))
+ });
+
+ builder.items.par_extend(items);
+ }
+ }
+
+ Some(())
+}
+
+fn find_citation(params: &CompletionParams) -> Option<Span> {
+ find_citation_command(params).or_else(|| find_citation_acronym(params))
+}
+
+fn find_citation_command(params: &CompletionParams) -> Option<Span> {
+ let (span, group) = find_curly_group_word_list(params)?;
+ latex::Citation::cast(group.syntax().parent()?)?;
+ Some(span)
+}
+
+fn find_citation_acronym(params: &CompletionParams) -> Option<Span> {
+ let offset = params.offset;
+ let data = params.feature.document.data.as_tex()?;
+ let root = data.root_node();
+ let tokens = root.token_at_offset(offset);
+ let token = tokens
+ .clone()
+ .find(|token| token.kind() == latex::WORD)
+ .or_else(|| tokens.left_biased())?;
+
+ let span = if token.kind() == latex::WORD {
+ let name = latex::Text::cast(token.parent()?)?;
+ Span::new(token.text().into(), latex::small_range(&name))
+ } else {
+ Span::empty(offset)
+ };
+
+ let pair = token
+ .parent_ancestors()
+ .find_map(latex::KeyValuePair::cast)?;
+
+ if pair.key()?.to_string() == "cite" {
+ let body = pair.syntax().parent()?;
+ let group = body.parent()?;
+ latex::AcronymDeclaration::cast(group.parent()?)?;
+ Some(span)
+ } else {
+ None
+ }
+}
diff --git a/support/texlab/crates/texlab/src/features/completion/color.rs b/support/texlab/crates/completion/src/providers/color.rs
index e415409844..cbdd38dc32 100644
--- a/support/texlab/crates/texlab/src/features/completion/color.rs
+++ b/support/texlab/crates/completion/src/providers/color.rs
@@ -1,25 +1,31 @@
use rowan::ast::AstNode;
use syntax::latex;
-use crate::util::cursor::CursorContext;
+use crate::{
+ util::{find_curly_group_word, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
+pub fn complete_colors<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
) -> Option<()> {
- let (_, range, group) = context.find_curly_group_word()?;
+ let (cursor, group) = find_curly_group_word(params)?;
latex::ColorReference::cast(group.syntax().parent()?)?;
for name in COLORS {
- builder.color(range, name);
+ if let Some(score) = builder.matcher.score(&name, &cursor.text) {
+ let data = CompletionItemData::Color(name);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
}
Some(())
}
-static COLORS: &[&str] = &[
+const COLORS: &[&str] = &[
"black",
"blue",
"brown",
diff --git a/support/texlab/crates/completion/src/providers/color_model.rs b/support/texlab/crates/completion/src/providers/color_model.rs
new file mode 100644
index 0000000000..4b991c4807
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/color_model.rs
@@ -0,0 +1,50 @@
+use base_db::semantics::Span;
+use rowan::ast::AstNode;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word, find_curly_group_word_list, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_color_models<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = check_color_definition(params).or_else(|| check_color_definition_set(params))?;
+
+ for name in MODEL_NAMES {
+ if let Some(score) = builder.matcher.score(&name, &cursor.text) {
+ let data = CompletionItemData::ColorModel(name);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+
+ Some(())
+}
+
+fn check_color_definition(params: &CompletionParams) -> Option<Span> {
+ let (span, group) = find_curly_group_word(params)?;
+
+ let definition = latex::ColorDefinition::cast(group.syntax().parent()?)?;
+ definition
+ .model()
+ .filter(|model| model.syntax().text_range() == group.syntax().text_range())?;
+
+ Some(span)
+}
+
+fn check_color_definition_set(params: &CompletionParams) -> Option<Span> {
+ let (span, group) = find_curly_group_word_list(params)?;
+
+ let definition = latex::ColorSetDefinition::cast(group.syntax().parent()?)?;
+ definition
+ .model_list()
+ .filter(|model| model.syntax().text_range() == group.syntax().text_range())?;
+
+ Some(span)
+}
+
+const MODEL_NAMES: &[&str] = &["gray", "rgb", "RGB", "HTML", "cmyk"];
diff --git a/support/texlab/crates/completion/src/providers/command.rs b/support/texlab/crates/completion/src/providers/command.rs
new file mode 100644
index 0000000000..ddbf8bc206
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/command.rs
@@ -0,0 +1,134 @@
+use base_db::{semantics::Span, DocumentData};
+use rowan::{TextRange, TextSize};
+use syntax::{bibtex, latex};
+
+use crate::{
+ util::{included_packages, CompletionBuilder, ProviderContext},
+ CommandData, CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_commands<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_command_name(params)?;
+
+ let mut proc = Processor(ProviderContext {
+ builder,
+ params,
+ cursor,
+ });
+
+ proc.add_begin_snippet();
+ proc.add_library();
+ proc.add_user();
+ Some(())
+}
+
+struct Processor<'a, 'b>(ProviderContext<'a, 'b>);
+
+impl<'a, 'b> Processor<'a, 'b> {
+ pub fn add_begin_snippet(&mut self) -> Option<()> {
+ let score = self.0.builder.matcher.score("begin", &self.0.cursor.text)?;
+ let data = CompletionItemData::BeginEnvironment;
+ self.0
+ .builder
+ .items
+ .push(CompletionItem::new_simple(score, self.0.cursor.range, data));
+
+ Some(())
+ }
+
+ pub fn add_library(&mut self) -> Option<()> {
+ for package in included_packages(&self.0.params.feature) {
+ let commands_with_score = package.commands.iter().filter_map(|command| {
+ let matcher = &self.0.builder.matcher;
+ let score = matcher.score(&command.name, &self.0.cursor.text)?;
+ Some((command, score))
+ });
+
+ for (command, score) in commands_with_score {
+ let data = CompletionItemData::Command(CommandData {
+ name: &command.name,
+ glyph: command.glyph.as_deref(),
+ image: command.image.as_deref(),
+ package: Some(package),
+ });
+
+ self.0.builder.items.push(CompletionItem::new_simple(
+ score,
+ self.0.cursor.range,
+ data,
+ ));
+ }
+ }
+
+ Some(())
+ }
+
+ fn add_user(&mut self) {
+ let documents = self.0.params.feature.project.documents.iter();
+ for data in documents.filter_map(|document| document.data.as_tex()) {
+ let commands = data
+ .semantics
+ .commands
+ .iter()
+ .filter(|name| name.range != self.0.cursor.range);
+
+ let commands_with_score = commands.filter_map(|command| {
+ let matcher = &self.0.builder.matcher;
+ let score = matcher.score(&command.text, &self.0.cursor.text)?;
+ Some((command, score))
+ });
+
+ for (command, score) in commands_with_score {
+ let data = CompletionItemData::Command(CommandData {
+ name: &command.text,
+ glyph: None,
+ image: None,
+ package: None,
+ });
+
+ self.0.builder.items.push(CompletionItem::new_simple(
+ score,
+ self.0.cursor.range,
+ data,
+ ));
+ }
+ }
+ }
+}
+
+fn find_command_name(params: &CompletionParams) -> Option<Span> {
+ let offset = params.offset;
+ match &params.feature.document.data {
+ DocumentData::Tex(data) => {
+ let root = data.root_node();
+ find_command_name_ast(&root, latex::COMMAND_NAME, offset)
+ }
+ DocumentData::Bib(data) => {
+ let root = data.root_node();
+ find_command_name_ast(&root, bibtex::COMMAND_NAME, offset)
+ .or_else(|| find_command_name_ast(&root, bibtex::ACCENT_NAME, offset))
+ }
+ _ => None,
+ }
+}
+
+fn find_command_name_ast<L: rowan::Language>(
+ root: &rowan::SyntaxNode<L>,
+ kind: L::Kind,
+ offset: TextSize,
+) -> Option<Span> {
+ let token = root
+ .token_at_offset(offset)
+ .filter(|token| token.text_range().start() != offset)
+ .find(|token| token.kind() == kind)?;
+
+ let full_range = token.text_range();
+ let text = String::from(&token.text()[1..]);
+ Some(Span::new(
+ text,
+ TextRange::new(full_range.start() + TextSize::of('\\'), full_range.end()),
+ ))
+}
diff --git a/support/texlab/crates/completion/src/providers/entry_type.rs b/support/texlab/crates/completion/src/providers/entry_type.rs
new file mode 100644
index 0000000000..ce8bffa51e
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/entry_type.rs
@@ -0,0 +1,40 @@
+use base_db::semantics::Span;
+use syntax::bibtex;
+
+use crate::{
+ util::CompletionBuilder, CompletionItem, CompletionItemData, CompletionParams, EntryTypeData,
+};
+
+pub fn complete_entry_types<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_entry_type(params)?;
+
+ for entry_type in base_db::data::BIBTEX_ENTRY_TYPES {
+ if let Some(score) = builder.matcher.score(entry_type.name, &cursor.text) {
+ let data = CompletionItemData::EntryType(EntryTypeData(*entry_type));
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+
+ Some(())
+}
+
+fn find_entry_type(params: &CompletionParams) -> Option<Span> {
+ let data = params.feature.document.data.as_bib()?;
+
+ let token = data
+ .root_node()
+ .token_at_offset(params.offset)
+ .find(|token| token.kind() == bibtex::TYPE)?;
+
+ let range = token.text_range();
+ if range.start() == params.offset {
+ None
+ } else {
+ Some(Span::from(&token))
+ }
+}
diff --git a/support/texlab/crates/completion/src/providers/environment.rs b/support/texlab/crates/completion/src/providers/environment.rs
new file mode 100644
index 0000000000..393dd100ba
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/environment.rs
@@ -0,0 +1,135 @@
+use base_db::semantics::Span;
+use rowan::ast::AstNode;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word, included_packages, CompletionBuilder, ProviderContext},
+ CompletionItem, CompletionItemData, CompletionParams, EnvironmentData,
+};
+
+pub fn complete_environments<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let (cursor, group) = find_environment(params)?;
+
+ let begin = group
+ .syntax()
+ .parent()
+ .and_then(|node| node.parent())
+ .and_then(latex::Environment::cast)
+ .and_then(|env| env.begin())
+ .and_then(|begin| begin.name())
+ .and_then(|name| name.key())
+ .map_or_else(|| String::new(), |name| name.to_string());
+
+ let mut proc = Processor {
+ inner: ProviderContext {
+ builder,
+ params,
+ cursor,
+ },
+ begin,
+ };
+
+ proc.add_library();
+ proc.add_theorem();
+ proc.add_user();
+ Some(())
+}
+
+struct Processor<'a, 'b> {
+ inner: ProviderContext<'a, 'b>,
+ begin: String,
+}
+
+impl<'a, 'b> Processor<'a, 'b> {
+ fn add_library(&mut self) {
+ for package in included_packages(&self.inner.params.feature) {
+ let envs_with_score = package.environments.iter().filter_map(|env| {
+ let matcher = &self.inner.builder.matcher;
+ let score = matcher.score(&env, &self.inner.cursor.text)?;
+ Some((*env, score))
+ });
+
+ for (name, score) in envs_with_score {
+ let data = CompletionItemData::Environment(EnvironmentData {
+ name,
+ package: Some(package),
+ });
+
+ self.inner.builder.items.push(CompletionItem {
+ score,
+ data,
+ range: self.inner.cursor.range,
+ preselect: name == self.begin,
+ });
+ }
+ }
+ }
+
+ fn add_theorem(&mut self) {
+ let documents = self.inner.params.feature.project.documents.iter();
+ for theorem in documents
+ .filter_map(|document| document.data.as_tex())
+ .flat_map(|data| data.semantics.theorem_definitions.iter())
+ {
+ let matcher = &self.inner.builder.matcher;
+ let name = theorem.name.text.as_str();
+ if let Some(score) = matcher.score(&name, &self.inner.cursor.text) {
+ let data = CompletionItemData::Environment(EnvironmentData {
+ name,
+ package: None,
+ });
+
+ self.inner.builder.items.push(CompletionItem {
+ score,
+ data,
+ range: self.inner.cursor.range,
+ preselect: name == self.begin,
+ });
+ }
+ }
+ }
+
+ fn add_user(&mut self) {
+ let documents = self.inner.params.feature.project.documents.iter();
+ for data in documents.filter_map(|document| document.data.as_tex()) {
+ let envs = data
+ .semantics
+ .environments
+ .iter()
+ .filter(|name| name.range != self.inner.cursor.range);
+
+ let envs_with_score = envs.filter_map(|env| {
+ let matcher = &self.inner.builder.matcher;
+ let score = matcher.score(&env.text, &self.inner.cursor.text)?;
+ Some((&env.text, score))
+ });
+
+ for (name, score) in envs_with_score {
+ let data = CompletionItemData::Environment(EnvironmentData {
+ name,
+ package: None,
+ });
+
+ self.inner.builder.items.push(CompletionItem {
+ score,
+ data,
+ range: self.inner.cursor.range,
+ preselect: name == &self.begin,
+ });
+ }
+ }
+ }
+}
+
+fn find_environment(params: &CompletionParams) -> Option<(Span, latex::CurlyGroupWord)> {
+ let (span, group) = find_curly_group_word(params)?;
+ let parent = group.syntax().parent()?;
+ if matches!(parent.kind(), latex::BEGIN | latex::END) {
+ Some((span, group))
+ } else {
+ None
+ }
+}
diff --git a/support/texlab/crates/completion/src/providers/field.rs b/support/texlab/crates/completion/src/providers/field.rs
new file mode 100644
index 0000000000..2dcd7fd26b
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/field.rs
@@ -0,0 +1,66 @@
+use base_db::semantics::Span;
+use rowan::{ast::AstNode, TokenAtOffset};
+use syntax::bibtex::{self, HasName};
+
+use crate::{
+ util::CompletionBuilder, CompletionItem, CompletionItemData, CompletionParams, FieldTypeData,
+};
+
+pub fn complete_fields<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_field(params)?;
+
+ for field in base_db::data::BIBTEX_FIELD_TYPES {
+ if let Some(score) = builder.matcher.score(field.name, &cursor.text) {
+ let data = CompletionItemData::Field(FieldTypeData(*field));
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+
+ Some(())
+}
+
+fn find_field(params: &CompletionParams) -> Option<Span> {
+ let token = select_token(params)?;
+ if token.kind() == bibtex::TYPE {
+ return None;
+ }
+
+ 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 {
+ bibtex::Field::cast(parent)?;
+ }
+
+ Some(if token.kind() == bibtex::NAME {
+ Span::from(&token)
+ } else {
+ Span::empty(params.offset)
+ })
+}
+
+fn select_token(params: &CompletionParams) -> Option<bibtex::SyntaxToken> {
+ let data = params.feature.document.data.as_bib()?;
+ Some(match data.root_node().token_at_offset(params.offset) {
+ TokenAtOffset::Between(_, r) if r.kind() == bibtex::TYPE => r,
+ TokenAtOffset::Between(l, _) if l.kind() == bibtex::TYPE => l,
+ TokenAtOffset::Between(l, _) if l.kind() == bibtex::COMMAND_NAME => l,
+ TokenAtOffset::Between(l, _) if l.kind() == bibtex::ACCENT_NAME => l,
+ TokenAtOffset::Between(_, r) if r.kind() == bibtex::WORD => r,
+ TokenAtOffset::Between(_, r) if r.kind() == bibtex::NAME => r,
+ TokenAtOffset::Between(l, _) if l.kind() == bibtex::WORD => l,
+ TokenAtOffset::Between(l, _) if l.kind() == bibtex::NAME => l,
+ TokenAtOffset::Between(_, r) if r.kind() == bibtex::COMMAND_NAME => r,
+ TokenAtOffset::Between(_, r) if r.kind() == bibtex::ACCENT_NAME => r,
+ TokenAtOffset::Between(_, r) => r,
+ TokenAtOffset::Single(t) => t,
+ TokenAtOffset::None => return None,
+ })
+}
diff --git a/support/texlab/crates/completion/src/providers/glossary.rs b/support/texlab/crates/completion/src/providers/glossary.rs
new file mode 100644
index 0000000000..c22f5b5888
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/glossary.rs
@@ -0,0 +1,88 @@
+use base_db::semantics::Span;
+use rowan::ast::AstNode;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word, CompletionBuilder, ProviderContext},
+ CompletionItem, CompletionItemData, CompletionParams, GlossaryEntryData,
+};
+
+pub fn complete_acronyms<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_acronym(params)?;
+ let mut proc = Processor(ProviderContext {
+ builder,
+ params,
+ cursor,
+ });
+
+ proc.add_acronyms();
+ Some(())
+}
+
+pub fn complete_glossaries<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let cursor = find_glossary(params)?;
+ let mut proc = Processor(ProviderContext {
+ builder,
+ params,
+ cursor,
+ });
+
+ proc.add_acronyms();
+ proc.add_glossaries();
+ Some(())
+}
+
+struct Processor<'a, 'b>(ProviderContext<'a, 'b>);
+
+impl<'a, 'b> Processor<'a, 'b> {
+ pub fn add_acronyms(&mut self) {
+ self.add_generic(|node| latex::AcronymDefinition::cast(node)?.name());
+ }
+
+ pub fn add_glossaries(&mut self) {
+ self.add_generic(|node| latex::GlossaryEntryDefinition::cast(node)?.name());
+ }
+
+ fn add_generic<F>(&mut self, extract: F)
+ where
+ F: Fn(latex::SyntaxNode) -> Option<latex::CurlyGroupWord>,
+ {
+ let documents = self.0.params.feature.project.documents.iter();
+ for data in documents.filter_map(|document| document.data.as_tex()) {
+ for name in data
+ .root_node()
+ .descendants()
+ .filter_map(|node| extract(node))
+ .filter_map(|name| name.key())
+ .map(|name| name.to_string())
+ {
+ if let Some(score) = self.0.builder.matcher.score(&name, &self.0.cursor.text) {
+ let data = CompletionItemData::GlossaryEntry(GlossaryEntryData { name });
+ self.0.builder.items.push(CompletionItem::new_simple(
+ score,
+ self.0.cursor.range,
+ data,
+ ));
+ }
+ }
+ }
+ }
+}
+
+fn find_acronym(params: &CompletionParams) -> Option<Span> {
+ let (cursor, group) = find_curly_group_word(params)?;
+ latex::AcronymReference::cast(group.syntax().parent()?)?;
+ Some(cursor)
+}
+
+fn find_glossary(params: &CompletionParams) -> Option<Span> {
+ let (cursor, group) = find_curly_group_word(params)?;
+ latex::GlossaryEntryReference::cast(group.syntax().parent()?)?;
+ Some(cursor)
+}
diff --git a/support/texlab/crates/completion/src/providers/import.rs b/support/texlab/crates/completion/src/providers/import.rs
new file mode 100644
index 0000000000..c506026a99
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/import.rs
@@ -0,0 +1,69 @@
+use rowan::ast::AstNode;
+use rustc_hash::FxHashSet;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word_list, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_imports<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let (cursor, group) = find_curly_group_word_list(params)?;
+
+ let kind = group.syntax().parent()?.kind();
+ let extension = match kind {
+ latex::PACKAGE_INCLUDE => "sty",
+ latex::CLASS_INCLUDE => "cls",
+ _ => return Some(()),
+ };
+
+ let mut file_names = FxHashSet::default();
+ for file_name in completion_data::DATABASE
+ .iter()
+ .flat_map(|package| package.file_names.iter())
+ .filter(|file_name| file_name.ends_with(extension))
+ {
+ file_names.insert(file_name);
+ let stem = &file_name[0..file_name.len() - 4];
+ if kind == latex::PACKAGE_INCLUDE {
+ if let Some(score) = builder.matcher.score(&stem, &cursor.text) {
+ let data = CompletionItemData::Package(stem);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ } else if let Some(score) = builder.matcher.score(&stem, &cursor.text) {
+ let data = CompletionItemData::DocumentClass(stem);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+
+ let file_name_db = &params.feature.workspace.distro().file_name_db;
+ for file_name in file_name_db
+ .iter()
+ .map(|(file_name, _)| file_name)
+ .filter(|file_name| file_name.ends_with(extension) && !file_names.contains(file_name))
+ {
+ let stem = &file_name[0..file_name.len() - 4];
+ if kind == latex::PACKAGE_INCLUDE {
+ if let Some(score) = builder.matcher.score(&stem, &cursor.text) {
+ let data = CompletionItemData::Package(stem);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ } else if let Some(score) = builder.matcher.score(&stem, &cursor.text) {
+ let data = CompletionItemData::DocumentClass(stem);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+
+ Some(())
+}
diff --git a/support/texlab/crates/texlab/src/features/completion/include.rs b/support/texlab/crates/completion/src/providers/include.rs
index 1cc1a0972b..2dd204cfdc 100644
--- a/support/texlab/crates/texlab/src/features/completion/include.rs
+++ b/support/texlab/crates/completion/src/providers/include.rs
@@ -1,26 +1,26 @@
use std::{
- convert::TryFrom,
fs,
path::{Path, PathBuf},
};
-use base_db::DocumentData;
-use rowan::{ast::AstNode, TextRange, TextSize};
+use base_db::{DocumentData, FeatureParams};
+use rowan::{ast::AstNode, TextLen, TextRange};
use syntax::latex;
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
+use crate::{
+ util::{find_curly_group_word_list, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
+pub fn complete_includes<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
) -> Option<()> {
- if context.document.path.is_none() {
+ if params.feature.document.path.is_none() {
return None;
}
- let (path_text, path_range, group) = context.find_curly_group_word_list()?;
+ let (cursor, group) = find_curly_group_word_list(params)?;
let include = group.syntax().parent()?;
let (include_extension, extensions): (bool, &[&str]) = match include.kind() {
@@ -42,20 +42,28 @@ pub fn complete<'db>(
_ => return None,
};
- let segment_range = if path_text.is_empty() {
- path_range
+ let segment_range = if cursor.text.is_empty() {
+ cursor.range
} else {
- let start =
- path_range.end() - TextSize::try_from(path_text.split('/').last()?.len()).ok()?;
- TextRange::new(start, path_range.end())
+ let start = cursor.range.end() - cursor.text.split('/').last()?.text_len();
+ TextRange::new(start, cursor.range.end())
};
- let mut dirs = vec![current_dir(context, &path_text, None)];
+ let segment_text = &params.feature.document.text[std::ops::Range::from(segment_range)];
+
+ let mut dirs = vec![current_dir(&params.feature, &cursor.text, None)];
if include.kind() == latex::GRAPHICS_INCLUDE {
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
+ for document in &params.feature.project.documents {
+ let DocumentData::Tex(data) = &document.data else {
+ continue;
+ };
+
for graphics_path in &data.semantics.graphics_paths {
- dirs.push(current_dir(context, &path_text, Some(graphics_path)));
+ dirs.push(current_dir(
+ &params.feature,
+ &cursor.text,
+ Some(graphics_path),
+ ));
}
}
}
@@ -75,11 +83,23 @@ pub fn complete<'db>(
remove_extension(&mut path);
}
- let name = path.file_name()?.to_str()?.into();
- builder.file(segment_range, name);
+ let name = String::from(path.file_name()?.to_str()?);
+ if let Some(score) = builder.matcher.score(&name, segment_text) {
+ builder.items.push(CompletionItem::new_simple(
+ score,
+ cursor.range,
+ CompletionItemData::File(name),
+ ));
+ }
} else if file_type.is_dir() {
- let name = path.file_name()?.to_str()?.into();
- builder.directory(segment_range, name);
+ let name = String::from(path.file_name()?.to_str()?);
+ if let Some(score) = builder.matcher.score(&name, segment_text) {
+ builder.items.push(CompletionItem::new_simple(
+ score,
+ cursor.range,
+ CompletionItemData::Directory(name),
+ ));
+ }
}
}
@@ -87,22 +107,18 @@ pub fn complete<'db>(
}
fn current_dir(
- context: &CursorContext,
+ params: &FeatureParams,
path_text: &str,
graphics_path: Option<&str>,
) -> Option<PathBuf> {
- let parent = context
- .workspace
- .parents(context.document)
+ let workspace = &params.workspace;
+ let parent = workspace
+ .parents(params.document)
.iter()
.next()
- .map_or(context.document, Clone::clone);
+ .map_or(params.document, Clone::clone);
- let path = context
- .workspace
- .current_dir(&parent.dir)
- .to_file_path()
- .ok()?;
+ let path = workspace.current_dir(&parent.dir).to_file_path().ok()?;
let mut path = PathBuf::from(path.to_str()?.replace('\\', "/"));
if !path_text.is_empty() {
@@ -115,6 +131,7 @@ fn current_dir(
path.pop();
}
}
+
Some(path)
}
diff --git a/support/texlab/crates/completion/src/providers/label.rs b/support/texlab/crates/completion/src/providers/label.rs
new file mode 100644
index 0000000000..8af2873457
--- /dev/null
+++ b/support/texlab/crates/completion/src/providers/label.rs
@@ -0,0 +1,101 @@
+use base_db::{
+ semantics::{tex::LabelKind, Span},
+ util::{render_label, RenderedObject},
+ DocumentData,
+};
+use rowan::ast::AstNode;
+use syntax::latex;
+
+use crate::{
+ util::{find_curly_group_word, find_curly_group_word_list, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
+
+pub fn complete_labels<'a>(
+ params: &'a CompletionParams<'a>,
+ builder: &mut CompletionBuilder<'a>,
+) -> Option<()> {
+ let FindResult { cursor, is_math } =
+ find_reference(params).or_else(|| find_reference_range(params))?;
+
+ for document in &params.feature.project.documents {
+ let DocumentData::Tex(data) = &document.data else {
+ continue;
+ };
+
+ for label in data
+ .semantics
+ .labels
+ .iter()
+ .filter(|label| label.kind == LabelKind::Definition)
+ {
+ match render_label(params.feature.workspace, &params.feature.project, label) {
+ Some(rendered_label) => {
+ if is_math && !matches!(rendered_label.object, RenderedObject::Equation) {
+ continue;
+ }
+
+ let header = rendered_label.detail();
+ let footer = match &rendered_label.object {
+ RenderedObject::Float { caption, .. } => Some(*caption),
+ _ => None,
+ };
+
+ let keywords = format!("{} {}", label.name.text, rendered_label.reference());
+
+ if let Some(score) = builder.matcher.score(&keywords, &cursor.text) {
+ let data = CompletionItemData::Label(crate::LabelData {
+ name: &label.name.text,
+ header,
+ footer,
+ object: Some(rendered_label.object),
+ keywords,
+ });
+
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+ None => {
+ if let Some(score) = builder.matcher.score(&label.name.text, &cursor.text) {
+ let data = CompletionItemData::Label(crate::LabelData {
+ name: &label.name.text,
+ header: None,
+ footer: None,
+ object: None,
+ keywords: label.name.text.clone(),
+ });
+
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
+ }
+ }
+ }
+ }
+ }
+
+ Some(())
+}
+
+struct FindResult {
+ cursor: Span,
+ is_math: bool,
+}
+
+fn find_reference(params: &CompletionParams) -> Option<FindResult> {
+ let (cursor, group) = find_curly_group_word_list(params)?;
+ let reference = latex::LabelReference::cast(group.syntax().parent()?)?;
+ let is_math = reference.command()?.text() == "\\eqref";
+ Some(FindResult { cursor, is_math })
+}
+
+fn find_reference_range(params: &CompletionParams) -> Option<FindResult> {
+ let (cursor, group) = find_curly_group_word(params)?;
+ latex::LabelReferenceRange::cast(group.syntax().parent()?)?;
+ Some(FindResult {
+ cursor,
+ is_math: false,
+ })
+}
diff --git a/support/texlab/crates/texlab/src/features/completion/tikz_library.rs b/support/texlab/crates/completion/src/providers/tikz_library.rs
index adb3fb9c66..10a0aa44ba 100644
--- a/support/texlab/crates/texlab/src/features/completion/tikz_library.rs
+++ b/support/texlab/crates/completion/src/providers/tikz_library.rs
@@ -1,25 +1,31 @@
use rowan::ast::AstNode;
use syntax::latex;
-use crate::util::cursor::CursorContext;
+use crate::{
+ util::{find_curly_group_word_list, CompletionBuilder},
+ CompletionItem, CompletionItemData, CompletionParams,
+};
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
+pub fn complete_tikz_libraries<'a>(
+ params: &'a CompletionParams,
+ builder: &mut CompletionBuilder<'a>,
) -> Option<()> {
- let (_, range, group) = context.find_curly_group_word_list()?;
+ let (cursor, group) = find_curly_group_word_list(params)?;
let import = latex::TikzLibraryImport::cast(group.syntax().parent()?)?;
- if import.command()?.text() == "\\usepgflibrary" {
- for name in PGF_LIBRARIES {
- builder.tikz_library(range, name);
- }
+ let libraries = if import.command()?.text() == "\\usepgflibrary" {
+ PGF_LIBRARIES
} else {
- for name in TIKZ_LIBRARIES {
- builder.tikz_library(range, name);
+ TIKZ_LIBRARIES
+ };
+
+ for name in libraries {
+ if let Some(score) = builder.matcher.score(&name, &cursor.text) {
+ let data = CompletionItemData::TikzLibrary(name);
+ builder
+ .items
+ .push(CompletionItem::new_simple(score, cursor.range, data));
}
}
diff --git a/support/texlab/crates/completion/src/tests.rs b/support/texlab/crates/completion/src/tests.rs
new file mode 100644
index 0000000000..84cd06447b
--- /dev/null
+++ b/support/texlab/crates/completion/src/tests.rs
@@ -0,0 +1,2071 @@
+use base_db::FeatureParams;
+use expect_test::{expect, Expect};
+use rowan::TextRange;
+
+use crate::CompletionParams;
+
+fn check(input: &str, expect: Expect) {
+ let fixture = test_utils::fixture::Fixture::parse(input);
+
+ let (offset, spec) = fixture
+ .documents
+ .iter()
+ .find_map(|document| Some((document.cursor?, document)))
+ .unwrap();
+
+ let document = fixture.workspace.lookup(&spec.uri).unwrap();
+ let feature = FeatureParams::new(&fixture.workspace, document);
+ let params = CompletionParams { feature, offset };
+ let result = crate::complete(&params);
+
+ let range = spec
+ .ranges
+ .first()
+ .map_or_else(|| TextRange::empty(offset), |range| *range);
+
+ for item in &result.items {
+ assert_eq!(item.range, range);
+ }
+
+ let items = result
+ .items
+ .into_iter()
+ .take(5)
+ .map(|item| item.data)
+ .collect::<Vec<_>>();
+
+ expect.assert_debug_eq(&items);
+}
+
+#[test]
+fn acronym_ref_simple() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{f}
+ |
+ ^"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn acronym_ref_empty() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{}
+ |"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn acronym_ref_after_group() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{}
+ |"#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
+fn acronym_ref_open_brace() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{f
+ |
+ ^"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn acronym_package_ref() {
+ check(
+ r#"
+%! main.tex
+\acrodef{fpsLabel}[FPS]{Frames per Second}
+\ac{f
+ |
+ ^"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn glossary_ref_simple() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\gls{f}
+ |
+ ^"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn glossary_ref_open_brace() {
+ check(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\gls{f
+ |
+ ^"#,
+ expect![[r#"
+ [
+ GlossaryEntry(
+ GlossaryEntryData {
+ name: "fpsLabel",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn argument_empty() {
+ check(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{}
+ |"#,
+ expect![[r#"
+ [
+ Argument(
+ ArgumentData(
+ "A",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "B",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "C",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "D",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "E",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn argument_word() {
+ check(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{A}
+ |
+ ^"#,
+ expect![[r#"
+ [
+ Argument(
+ ArgumentData(
+ "A",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn argument_open_brace() {
+ check(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{
+ |
+Test"#,
+ expect![[r#"
+ [
+ Argument(
+ ArgumentData(
+ "A",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "B",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "C",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "D",
+ ),
+ ),
+ Argument(
+ ArgumentData(
+ "E",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn argument_open_brace_unrelated() {
+ check(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{}{
+ |
+Test"#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
+fn begin_environment_without_snippet_support() {
+ check(
+ r#"
+%! main.tex
+\beg
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ BeginEnvironment,
+ Command(
+ CommandData {
+ name: "begingroup",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "AtBeginDocument",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "AtBeginDvi",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "bigwedge",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn citation() {
+ check(
+ r#"
+%! main.tex
+\documentclass{article}
+\bibliography{main}
+\begin{document}
+\cite{
+ |
+\end{document}
+
+%! main.bib
+@article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+}
+
+@article{bar:2005,}"#,
+ expect![[r#"
+ [
+ Citation(
+ CitationData {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ entry: Entry {
+ name: Span(
+ "bar:2005",
+ 97..105,
+ ),
+ full_range: 88..107,
+ keywords: "bar:2005 @article",
+ category: Article,
+ },
+ },
+ ),
+ Citation(
+ CitationData {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ entry: Entry {
+ name: Span(
+ "foo:2019",
+ 9..17,
+ ),
+ full_range: 0..86,
+ keywords: "foo:2019 @article Foo Bar Baz Qux 2019",
+ category: Article,
+ },
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn citation_open_brace() {
+ check(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\cite{
+ |
+
+%! main.bib
+@article{foo,}"#,
+ expect![[r#"
+ [
+ Citation(
+ CitationData {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ entry: Entry {
+ name: Span(
+ "foo",
+ 9..12,
+ ),
+ full_range: 0..14,
+ keywords: "foo @article",
+ category: Article,
+ },
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn citation_open_brace_multiple() {
+ check(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo,f
+ |
+ ^
+
+%! main.bib
+@article{foo,}"#,
+ expect![[r#"
+ [
+ Citation(
+ CitationData {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ entry: Entry {
+ name: Span(
+ "foo",
+ 9..12,
+ ),
+ full_range: 0..14,
+ keywords: "foo @article",
+ category: Article,
+ },
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn citation_acronym() {
+ check(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\DeclareAcronym{foo}{cite={}}
+ |
+
+%! main.bib
+@article{foo,}"#,
+ expect![[r#"
+ [
+ Citation(
+ CitationData {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ entry: Entry {
+ name: Span(
+ "foo",
+ 9..12,
+ ),
+ full_range: 0..14,
+ keywords: "foo @article",
+ category: Article,
+ },
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn citation_after_brace() {
+ check(
+ r#"
+%! main.tex
+\documentclass{article}
+\bibliography{main}
+\begin{document}
+\cite{}
+ |
+\end{document}
+
+%! main.bib
+@article{foo,}"#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
+fn color_model_definition_simple() {
+ check(
+ r#"
+%! main.tex
+\definecolor{foo}{}
+ |"#,
+ expect![[r#"
+ [
+ ColorModel(
+ "HTML",
+ ),
+ ColorModel(
+ "RGB",
+ ),
+ ColorModel(
+ "cmyk",
+ ),
+ ColorModel(
+ "gray",
+ ),
+ ColorModel(
+ "rgb",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_model_definition_open_brace() {
+ check(
+ r#"
+%! main.tex
+\definecolor{foo}{
+ |"#,
+ expect![[r#"
+ [
+ ColorModel(
+ "HTML",
+ ),
+ ColorModel(
+ "RGB",
+ ),
+ ColorModel(
+ "cmyk",
+ ),
+ ColorModel(
+ "gray",
+ ),
+ ColorModel(
+ "rgb",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_model_definition_set_simple() {
+ check(
+ r#"
+%! main.tex
+\definecolorset{}
+ |"#,
+ expect![[r#"
+ [
+ ColorModel(
+ "HTML",
+ ),
+ ColorModel(
+ "RGB",
+ ),
+ ColorModel(
+ "cmyk",
+ ),
+ ColorModel(
+ "gray",
+ ),
+ ColorModel(
+ "rgb",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_model_definition_set_open_brace() {
+ check(
+ r#"
+%! main.tex
+\definecolorset{
+ |"#,
+ expect![[r#"
+ [
+ ColorModel(
+ "HTML",
+ ),
+ ColorModel(
+ "RGB",
+ ),
+ ColorModel(
+ "cmyk",
+ ),
+ ColorModel(
+ "gray",
+ ),
+ ColorModel(
+ "rgb",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_simple() {
+ check(
+ r#"
+%! main.tex
+\color{}
+ |"#,
+ expect![[r#"
+ [
+ Color(
+ "Apricot",
+ ),
+ Color(
+ "Aquamarine",
+ ),
+ Color(
+ "Bittersweet",
+ ),
+ Color(
+ "Black",
+ ),
+ Color(
+ "Blue",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_word() {
+ check(
+ r#"
+%! main.tex
+\color{re}
+ |
+ ^^"#,
+ expect![[r#"
+ [
+ Color(
+ "red",
+ ),
+ Color(
+ "Red",
+ ),
+ Color(
+ "RedOrange",
+ ),
+ Color(
+ "RedViolet",
+ ),
+ Color(
+ "BrickRed",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn color_open_brace() {
+ check(
+ r#"
+%! main.tex
+\color{
+ |"#,
+ expect![[r#"
+ [
+ Color(
+ "Apricot",
+ ),
+ Color(
+ "Aquamarine",
+ ),
+ Color(
+ "Bittersweet",
+ ),
+ Color(
+ "Black",
+ ),
+ Color(
+ "Blue",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_command_simple() {
+ check(
+ r#"
+%! main.tex
+\
+ |"#,
+ expect![[r##"
+ [
+ Command(
+ CommandData {
+ name: "!",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "\"",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "#",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "$",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "%",
+ package: [],
+ },
+ ),
+ ]
+ "##]],
+ );
+}
+
+#[test]
+fn component_command_simple_before() {
+ check(
+ r#"
+%! main.tex
+\
+|"#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
+fn component_command_simple_package() {
+ check(
+ r#"
+%! main.tex
+\usepackage{lipsum}
+\lips
+ |
+ ^^^^"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "lipsum",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "lipsumexp",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumPar",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumProtect",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumRestoreAll",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_command_bibtex() {
+ check(
+ r#"
+%! main.bib
+@article{b,
+ c = {\LaT }
+ |
+ ^^^
+}"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "LaTeX",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LaTeXe",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "latexreleaseversion",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LastDeclaredEncoding",
+ package: [],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "last",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_environment_simple() {
+ check(
+ r#"
+%! main.tex
+\begin{doc
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "document",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_environment_simple_end() {
+ check(
+ r#"
+%! main.tex
+\begin{document}
+\end{
+ |"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "document",
+ package: [],
+ },
+ ),
+ Environment(
+ EnvironmentData {
+ name: "abstract",
+ package: [],
+ },
+ ),
+ Environment(
+ EnvironmentData {
+ name: "array",
+ package: [],
+ },
+ ),
+ Environment(
+ EnvironmentData {
+ name: "center",
+ package: [],
+ },
+ ),
+ Environment(
+ EnvironmentData {
+ name: "csname",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_environment_class() {
+ check(
+ r#"
+%! main.tex
+\documentclass{article}
+\begin{thein}
+ |
+ ^^^^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "theindex",
+ package: [
+ "article.cls",
+ ],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn component_environment_command_definition() {
+ check(
+ r#"
+%! main.tex
+\newcommand{\foo}{\begin{doc}
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "document",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_at_empty() {
+ check(
+ r#"
+%! main.bib
+@
+ |
+^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@article",
+ ),
+ ),
+ EntryType(
+ EntryTypeData(
+ "@artwork",
+ ),
+ ),
+ EntryType(
+ EntryTypeData(
+ "@audio",
+ ),
+ ),
+ EntryType(
+ EntryTypeData(
+ "@bibnote",
+ ),
+ ),
+ EntryType(
+ EntryTypeData(
+ "@book",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_before_preamble() {
+ check(
+ r#"
+%! main.bib
+@preamble
+ |
+^^^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@preamble",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_before_string() {
+ check(
+ r#"
+%! main.bib
+@string
+ |
+^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@string",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_before_article() {
+ check(
+ r#"
+%! main.bib
+@article
+ |
+^^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@article",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_after_preamble() {
+ check(
+ r#"
+%! main.bib
+@preamble{
+ |
+^^^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@preamble",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_after_string() {
+ check(
+ r#"
+%! main.bib
+@string{
+ |
+^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@string",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn entry_type_complete_entry() {
+ check(
+ r#"
+%! main.bib
+@article{foo, author = {foo}}
+ |
+^^^^^^^^"#,
+ expect![[r#"
+ [
+ EntryType(
+ EntryTypeData(
+ "@article",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn field_empty_entry_open() {
+ check(
+ r#"
+%! main.bib
+@article{foo,
+ |"#,
+ expect![[r#"
+ [
+ Field(
+ FieldTypeData(
+ "abstract",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "addendum",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "address",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "afterword",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "annotation",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn field_empty_entry_closed() {
+ check(
+ r#"
+%! main.bib
+@article{foo,}
+ |"#,
+ expect![[r#"
+ [
+ Field(
+ FieldTypeData(
+ "abstract",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "addendum",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "address",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "afterword",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "annotation",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn field_entry_field_name() {
+ check(
+ r#"
+%! main.bib
+@article{foo, a
+ |
+ ^"#,
+ expect![[r#"
+ [
+ Field(
+ FieldTypeData(
+ "abstract",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "addendum",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "address",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "afterword",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "annotation",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn field_entry_two_fields_name_open() {
+ check(
+ r#"
+%! main.bib
+@article{foo, author = bar, edit
+ |
+ ^^^^"#,
+ expect![[r#"
+ [
+ Field(
+ FieldTypeData(
+ "edition",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editor",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editora",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editoratype",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editorb",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn field_entry_two_fields_name_closed() {
+ check(
+ r#"
+%! main.bib
+@article{foo, author = bar, edit}
+ |
+ ^^^^"#,
+ expect![[r#"
+ [
+ Field(
+ FieldTypeData(
+ "edition",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editor",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editora",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editoratype",
+ ),
+ ),
+ Field(
+ FieldTypeData(
+ "editorb",
+ ),
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn import_package_open_brace() {
+ check(
+ r#"
+%! main.tex
+\usepackage{lips
+ |
+ ^^^^"#,
+ expect![[r#"
+ [
+ Package(
+ "lips",
+ ),
+ Package(
+ "lipsum",
+ ),
+ Package(
+ "lisp-simple-alloc",
+ ),
+ Package(
+ "lisp-string",
+ ),
+ Package(
+ "lwarp-lips",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn import_package_closed_brace() {
+ check(
+ r#"
+%! main.tex
+\usepackage{lips}
+ |
+ ^^^^"#,
+ expect![[r#"
+ [
+ Package(
+ "lips",
+ ),
+ Package(
+ "lipsum",
+ ),
+ Package(
+ "lisp-simple-alloc",
+ ),
+ Package(
+ "lisp-string",
+ ),
+ Package(
+ "lwarp-lips",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn import_class_open_brace() {
+ check(
+ r#"
+%! main.tex
+\documentclass{art \foo
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ DocumentClass(
+ "article",
+ ),
+ DocumentClass(
+ "articleingud",
+ ),
+ DocumentClass(
+ "articoletteracdp",
+ ),
+ DocumentClass(
+ "artikel1",
+ ),
+ DocumentClass(
+ "artikel2",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn import_class_closed_brace() {
+ check(
+ r#"
+%! main.tex
+\documentclass{art}
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ DocumentClass(
+ "article",
+ ),
+ DocumentClass(
+ "articleingud",
+ ),
+ DocumentClass(
+ "articoletteracdp",
+ ),
+ DocumentClass(
+ "artikel1",
+ ),
+ DocumentClass(
+ "artikel2",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn label() {
+ check(
+ r#"
+%! foo.tex
+\documentclass{article}
+
+\usepackage{amsmath}
+\usepackage{caption}
+\usepackage{amsthm}
+\newtheorem{lemma}{Lemma}
+
+\begin{document}
+
+\section{Foo}%
+\label{sec:foo}
+
+\begin{equation}%
+\label{eq:foo}
+ 1 + 1 = 2
+\end{equation}
+
+\begin{equation}%
+\label{eq:bar}
+ 1 + 1 = 2
+\end{equation}
+
+\begin{figure}%
+\LaTeX{}
+\caption{Baz}%
+\label{fig:baz}
+\end{figure}
+
+\begin{lemma}%
+\label{thm:foo}
+ 1 + 1 = 2
+\end{lemma}
+
+\include{bar}
+
+\end{document}
+
+%! bar.tex
+\section{Bar}%
+\label{sec:bar}
+
+Lorem ipsum dolor sit amet.
+\ref{}
+ |
+
+%! foo.aux
+\relax
+\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
+\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+\newlabel{fig:baz}{{1}{1}}
+\@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+\newlabel{sec:foo}{{1}{1}}
+\newlabel{eq:foo}{{1}{1}}
+\newlabel{eq:bar}{{2}{1}}
+\newlabel{thm:foo}{{1}{1}}
+\@input{bar.aux}"#,
+ expect![[r#"
+ [
+ Label(
+ LabelData {
+ name: "eq:bar",
+ header: Some(
+ "Equation (2)",
+ ),
+ footer: None,
+ object: Some(
+ Equation,
+ ),
+ keywords: "eq:bar Equation (2)",
+ },
+ ),
+ Label(
+ LabelData {
+ name: "eq:foo",
+ header: Some(
+ "Equation (1)",
+ ),
+ footer: None,
+ object: Some(
+ Equation,
+ ),
+ keywords: "eq:foo Equation (1)",
+ },
+ ),
+ Label(
+ LabelData {
+ name: "fig:baz",
+ header: Some(
+ "Figure 1",
+ ),
+ footer: Some(
+ "Baz",
+ ),
+ object: Some(
+ Float {
+ kind: Figure,
+ caption: "Baz",
+ },
+ ),
+ keywords: "fig:baz Figure 1: Baz",
+ },
+ ),
+ Label(
+ LabelData {
+ name: "sec:bar",
+ header: Some(
+ "Section (Bar)",
+ ),
+ footer: None,
+ object: Some(
+ Section {
+ prefix: "Section",
+ text: "Bar",
+ },
+ ),
+ keywords: "sec:bar Section (Bar)",
+ },
+ ),
+ Label(
+ LabelData {
+ name: "sec:foo",
+ header: Some(
+ "Section 1 (Foo)",
+ ),
+ footer: None,
+ object: Some(
+ Section {
+ prefix: "Section",
+ text: "Foo",
+ },
+ ),
+ keywords: "sec:foo Section 1 (Foo)",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn theorem_begin() {
+ check(
+ r#"
+%! main.tex
+\newtheorem{lemma}{Lemma}
+\begin{lem
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "lemma",
+ package: "<user>",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn theorem_end() {
+ check(
+ r#"
+%! main.tex
+\newtheorem{lemma}{Lemma}
+\begin{}
+\end{lem
+ |
+ ^^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "lemma",
+ package: "<user>",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn tikz_library_open_brace() {
+ check(
+ r#"
+%! main.tex
+\usepgflibrary{
+ |"#,
+ expect![[r#"
+ [
+ TikzLibrary(
+ "arrows",
+ ),
+ TikzLibrary(
+ "arrows.meta",
+ ),
+ TikzLibrary(
+ "arrows.spaced",
+ ),
+ TikzLibrary(
+ "curvilinear",
+ ),
+ TikzLibrary(
+ "datavisualization.barcharts",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn tikz_library_closed_brace() {
+ check(
+ r#"
+%! main.tex
+\usepgflibrary{}
+ |"#,
+ expect![[r#"
+ [
+ TikzLibrary(
+ "arrows",
+ ),
+ TikzLibrary(
+ "arrows.meta",
+ ),
+ TikzLibrary(
+ "arrows.spaced",
+ ),
+ TikzLibrary(
+ "curvilinear",
+ ),
+ TikzLibrary(
+ "datavisualization.barcharts",
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn test_user_command() {
+ check(
+ r#"
+%! main.tex
+\foobar
+\fooba
+ |
+ ^^^^^
+\begin{foo}
+\end{foo}
+\begin{fo}"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "foobar",
+ package: "<user>",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn test_user_environment() {
+ check(
+ r#"
+%! main.tex
+\foobar
+\fooba
+\begin{foo}
+\end{foo}
+\begin{fo}
+ |
+ ^^"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "foo",
+ package: "<user>",
+ },
+ ),
+ Environment(
+ EnvironmentData {
+ name: "filecontents",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn test_project_resolution_import() {
+ check(
+ r#"
+%! main.tex
+\documentclass{article}
+\import{sub}{sub.tex}
+\lipsu
+ |
+ ^^^^^
+
+%! sub/sub.tex
+\input{child.tex}
+
+%! sub/child.tex
+\usepackage{lipsum}"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "lipsum",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "lipsumexp",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumPar",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumProtect",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumRestoreAll",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn test_project_resolution_texlabroot() {
+ check(
+ r#"
+%! src/main.tex
+\documentclass{article}
+\include{src/foo}
+\lipsu
+ |
+ ^^^^^
+
+%! src/foo.tex
+\include{src/bar}
+
+%! src/bar.tex
+\usepackage{lipsum}
+
+%! .texlabroot"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "lipsum",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "lipsumexp",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumPar",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumProtect",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ Command(
+ CommandData {
+ name: "LipsumRestoreAll",
+ package: [
+ "lipsum.sty",
+ ],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn issue_857_1() {
+ check(
+ r#"
+%! bug.tex
+\documentclass{article}
+\newcommand{\ö}{foo}
+\newcommand{\öö}{bar}
+\newcommand{\ööabc}{baz}
+\begin{document}
+\ö
+ |
+ ^
+\end{document}
+"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "ö",
+ package: "<user>",
+ },
+ ),
+ Command(
+ CommandData {
+ name: "öö",
+ package: "<user>",
+ },
+ ),
+ Command(
+ CommandData {
+ name: "ööabc",
+ package: "<user>",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn issue_864() {
+ check(
+ r#"
+%! bug.tex
+\documentclass{article}
+\def\あいうえお{}
+\begin{document}
+\あ
+ |
+ ^
+\end{document}"#,
+ expect![[r#"
+ [
+ Command(
+ CommandData {
+ name: "あいうえお",
+ package: "<user>",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn issue_883() {
+ check(
+ r#"
+%! bug.tex
+\begin{doc
+ |
+ ^^^
+% Comment"#,
+ expect![[r#"
+ [
+ Environment(
+ EnvironmentData {
+ name: "document",
+ package: [],
+ },
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn issue_885() {
+ check(
+ r#"
+%! main.tex
+\documentclass{book}
+\usepackage{import}
+\begin{document}
+\subincludefrom{part 1}{main}
+\include{part 2/main}
+
+\ref{sec}
+ |
+ ^^^
+\end{document}
+
+%! part 1/main.tex
+\part{1}
+\label{part 1}
+\subimport{chapter 1}{main}
+
+%! part 1/chapter 1/main.tex
+\chapter{1}
+\label{chapter 1}
+\subimport{./}{section 1}
+%\subimport{}{section 1}
+
+%! part 1/chapter 1/section 1.tex
+\section{1}
+\label{section 1}
+
+%! part 2/main.tex
+\part{2}
+\label{part 2}
+\input{part 2/chapter 2/main}
+
+%! part 2/chapter 2/main.tex
+\chapter{2}
+\label{chapter 2}
+\input{part 2/chapter 2/section 2}
+
+%! part 2/chapter 2/section 2.tex
+\section{2}
+\label{section 2}
+"#,
+ expect![[r#"
+ [
+ Label(
+ LabelData {
+ name: "section 1",
+ header: Some(
+ "Section (1)",
+ ),
+ footer: None,
+ object: Some(
+ Section {
+ prefix: "Section",
+ text: "1",
+ },
+ ),
+ keywords: "section 1 Section (1)",
+ },
+ ),
+ Label(
+ LabelData {
+ name: "section 2",
+ header: Some(
+ "Section (2)",
+ ),
+ footer: None,
+ object: Some(
+ Section {
+ prefix: "Section",
+ text: "2",
+ },
+ ),
+ keywords: "section 2 Section (2)",
+ },
+ ),
+ ]
+ "#]],
+ );
+}
diff --git a/support/texlab/crates/completion/src/util.rs b/support/texlab/crates/completion/src/util.rs
new file mode 100644
index 0000000000..6e64866979
--- /dev/null
+++ b/support/texlab/crates/completion/src/util.rs
@@ -0,0 +1,33 @@
+mod builder;
+pub mod matchers;
+mod patterns;
+
+pub use builder::*;
+pub use patterns::*;
+
+pub fn included_packages<'a>(
+ params: &'a base_db::FeatureParams<'a>,
+) -> impl Iterator<Item = &completion_data::Package<'_>> + 'a {
+ let db = &completion_data::DATABASE;
+ let documents = params.project.documents.iter();
+ let links = documents
+ .filter_map(|document| document.data.as_tex())
+ .flat_map(|data| data.semantics.links.iter());
+
+ links
+ .filter_map(|link| link.package_name())
+ .filter_map(|name| db.find(&name))
+ .chain(std::iter::once(db.kernel()))
+ .flat_map(|pkg| {
+ pkg.references
+ .iter()
+ .filter_map(|name| db.find(name))
+ .chain(std::iter::once(pkg))
+ })
+}
+
+pub struct ProviderContext<'a, 'b> {
+ pub builder: &'b mut CompletionBuilder<'a>,
+ pub params: &'a crate::CompletionParams<'a>,
+ pub cursor: base_db::semantics::Span,
+}
diff --git a/support/texlab/crates/completion/src/util/builder.rs b/support/texlab/crates/completion/src/util/builder.rs
new file mode 100644
index 0000000000..4ae0818e58
--- /dev/null
+++ b/support/texlab/crates/completion/src/util/builder.rs
@@ -0,0 +1,43 @@
+use base_db::{MatchingAlgo, Workspace};
+use fuzzy_matcher::skim::SkimMatcherV2;
+
+use crate::{CompletionItem, CompletionResult};
+
+use super::matchers::{self, Matcher};
+
+pub struct CompletionBuilder<'a> {
+ pub matcher: Box<dyn Matcher>,
+ pub items: Vec<CompletionItem<'a>>,
+}
+
+impl<'a> From<&Workspace> for CompletionBuilder<'a> {
+ fn from(workspace: &Workspace) -> Self {
+ let matcher: Box<dyn Matcher> = match workspace.config().completion.matcher {
+ MatchingAlgo::Skim => Box::<SkimMatcherV2>::default(),
+ MatchingAlgo::SkimIgnoreCase => Box::new(SkimMatcherV2::default().ignore_case()),
+ MatchingAlgo::Prefix => Box::new(matchers::Prefix),
+ MatchingAlgo::PrefixIgnoreCase => Box::new(matchers::PrefixIgnoreCase),
+ };
+
+ Self {
+ matcher,
+ items: Vec::new(),
+ }
+ }
+}
+
+impl<'a> CompletionBuilder<'a> {
+ pub fn finish(mut self) -> CompletionResult<'a> {
+ self.items.sort_by(|a, b| {
+ b.preselect
+ .cmp(&a.preselect)
+ .then_with(|| b.score.cmp(&a.score))
+ .then_with(|| a.data.label().cmp(b.data.label()))
+ });
+
+ self.items.dedup_by(|a, b| a.data.label() == b.data.label());
+ self.items.truncate(crate::LIMIT);
+ let Self { items, .. } = self;
+ CompletionResult { items }
+ }
+}
diff --git a/support/texlab/crates/texlab/src/features/completion/matcher.rs b/support/texlab/crates/completion/src/util/matchers.rs
index fb53d1c032..fb53d1c032 100644
--- a/support/texlab/crates/texlab/src/features/completion/matcher.rs
+++ b/support/texlab/crates/completion/src/util/matchers.rs
diff --git a/support/texlab/crates/completion/src/util/patterns.rs b/support/texlab/crates/completion/src/util/patterns.rs
new file mode 100644
index 0000000000..60bbaa526f
--- /dev/null
+++ b/support/texlab/crates/completion/src/util/patterns.rs
@@ -0,0 +1,75 @@
+use base_db::semantics::Span;
+use rowan::{ast::AstNode, TextRange, TextSize};
+use syntax::latex;
+
+use crate::CompletionParams;
+
+pub fn find_curly_group_word(params: &CompletionParams) -> Option<(Span, latex::CurlyGroupWord)> {
+ let offset = params.offset;
+ let data = params.feature.document.data.as_tex()?;
+ let root = data.root_node();
+ let tokens = root.token_at_offset(offset);
+ let token = tokens
+ .clone()
+ .find(|token| token.kind() == latex::WORD)
+ .or_else(|| tokens.left_biased())?;
+
+ let key = latex::Key::cast(token.parent()?);
+
+ let group = key
+ .as_ref()
+ .and_then(|key| key.syntax().parent())
+ .unwrap_or(token.parent()?);
+
+ let group =
+ latex::CurlyGroupWord::cast(group).filter(|group| is_inside_latex_curly(group, offset))?;
+
+ let span = key.map_or_else(|| Span::empty(offset), |key| Span::from(&key));
+ Some((span, group))
+}
+
+pub fn find_curly_group_word_list(
+ params: &CompletionParams,
+) -> Option<(Span, latex::CurlyGroupWordList)> {
+ let offset = params.offset;
+ let data = params.feature.document.data.as_tex()?;
+ let root = data.root_node();
+ let tokens = root.token_at_offset(offset);
+ let token = tokens
+ .clone()
+ .find(|token| token.kind() == latex::WORD)
+ .or_else(|| tokens.left_biased())?;
+
+ let key = latex::Key::cast(token.parent()?);
+
+ let group = key
+ .as_ref()
+ .and_then(|key| key.syntax().parent())
+ .unwrap_or(token.parent()?);
+
+ let group = latex::CurlyGroupWordList::cast(group)
+ .filter(|group| is_inside_latex_curly(group, offset))?;
+
+ let span = key.map_or_else(
+ || Span::empty(offset),
+ |key| {
+ let range = if group
+ .syntax()
+ .last_token()
+ .map_or(false, |tok| tok.kind() != latex::R_CURLY)
+ {
+ TextRange::new(latex::small_range(&key).start(), token.text_range().end())
+ } else {
+ latex::small_range(&key)
+ };
+
+ Span::new(token.text().into(), range)
+ },
+ );
+
+ Some((span, group))
+}
+
+pub fn is_inside_latex_curly(group: &impl latex::HasCurly, offset: TextSize) -> bool {
+ latex::small_range(group).contains(offset) || group.right_curly().is_none()
+}
diff --git a/support/texlab/crates/definition/Cargo.toml b/support/texlab/crates/definition/Cargo.toml
index 788abfc118..800ca7f6da 100644
--- a/support/texlab/crates/definition/Cargo.toml
+++ b/support/texlab/crates/definition/Cargo.toml
@@ -9,6 +9,7 @@ rust-version.workspace = true
[dependencies]
base-db = { path = "../base-db" }
rowan = "0.15.11"
+rustc-hash = "1.1.0"
syntax = { path = "../syntax" }
[dev-dependencies]
diff --git a/support/texlab/crates/definition/src/citation.rs b/support/texlab/crates/definition/src/citation.rs
index 51550a585d..509b008b26 100644
--- a/support/texlab/crates/definition/src/citation.rs
+++ b/support/texlab/crates/definition/src/citation.rs
@@ -18,7 +18,7 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> {
let name = citation.object.name_text();
for (document, entry) in queries::objects_with_name::<bib::Entry>(&context.project, name) {
- context.results.push(DefinitionResult {
+ context.results.insert(DefinitionResult {
origin_selection_range: citation.object.name_range(),
target: document,
target_range: entry.full_range,
diff --git a/support/texlab/crates/definition/src/label.rs b/support/texlab/crates/definition/src/label.rs
index 3c8c922059..80f1442daf 100644
--- a/support/texlab/crates/definition/src/label.rs
+++ b/support/texlab/crates/definition/src/label.rs
@@ -25,7 +25,7 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> {
let target_range = render_label(context.params.workspace, &context.project, label)
.map_or(target_selection_range, |label| label.range);
- context.results.push(DefinitionResult {
+ context.results.insert(DefinitionResult {
origin_selection_range: reference.object.name_range(),
target: document,
target_range,
diff --git a/support/texlab/crates/definition/src/lib.rs b/support/texlab/crates/definition/src/lib.rs
index 8678bb0016..2c1614f04e 100644
--- a/support/texlab/crates/definition/src/lib.rs
+++ b/support/texlab/crates/definition/src/lib.rs
@@ -6,6 +6,7 @@ mod string_ref;
use base_db::{Document, Project, Workspace};
use rowan::{TextRange, TextSize};
+use rustc_hash::FxHashSet;
#[derive(Debug)]
pub struct DefinitionParams<'db> {
@@ -14,7 +15,7 @@ pub struct DefinitionParams<'db> {
pub offset: TextSize,
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct DefinitionResult<'a> {
pub origin_selection_range: TextRange,
pub target: &'a Document,
@@ -26,15 +27,15 @@ pub struct DefinitionResult<'a> {
struct DefinitionContext<'db> {
params: DefinitionParams<'db>,
project: Project<'db>,
- results: Vec<DefinitionResult<'db>>,
+ results: FxHashSet<DefinitionResult<'db>>,
}
-pub fn goto_definition(params: DefinitionParams) -> Vec<DefinitionResult> {
+pub fn goto_definition(params: DefinitionParams) -> FxHashSet<DefinitionResult> {
let project = params.workspace.project(params.document);
let mut context = DefinitionContext {
params,
project,
- results: Vec::new(),
+ results: FxHashSet::default(),
};
command::goto_definition(&mut context);
diff --git a/support/texlab/crates/definition/src/string_ref.rs b/support/texlab/crates/definition/src/string_ref.rs
index 883b126cc0..678fc5911e 100644
--- a/support/texlab/crates/definition/src/string_ref.rs
+++ b/support/texlab/crates/definition/src/string_ref.rs
@@ -23,7 +23,7 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> {
.iter()
.filter(|string| string.name.text == name.text())
{
- context.results.push(DefinitionResult {
+ context.results.insert(DefinitionResult {
origin_selection_range,
target: context.params.document,
target_range: string.full_range,
diff --git a/support/texlab/crates/definition/src/tests.rs b/support/texlab/crates/definition/src/tests.rs
index e1bf68ec2f..8c08be72d1 100644
--- a/support/texlab/crates/definition/src/tests.rs
+++ b/support/texlab/crates/definition/src/tests.rs
@@ -1,3 +1,5 @@
+use rustc_hash::FxHashSet;
+
use crate::{DefinitionParams, DefinitionResult};
fn check(input: &str) {
@@ -23,7 +25,7 @@ fn check(input: &str) {
let origin_document = origin_document.unwrap();
- let mut expected = Vec::new();
+ let mut expected = FxHashSet::default();
for document in &fixture.documents {
let mut ranges = document.ranges.iter();
while let Some(target_selection_range) = ranges.next().copied() {
@@ -31,7 +33,7 @@ fn check(input: &str) {
if (&origin_document.uri, origin_selection_range)
!= (&document.uri, target_selection_range)
{
- expected.push(DefinitionResult {
+ expected.insert(DefinitionResult {
origin_selection_range,
target: fixture.workspace.lookup(&document.uri).unwrap(),
target_range: *ranges.next().unwrap(),
@@ -41,26 +43,15 @@ fn check(input: &str) {
}
}
- let mut actual = crate::goto_definition(DefinitionParams {
+ let actual = crate::goto_definition(DefinitionParams {
workspace,
document: workspace.lookup(&origin_document.uri).unwrap(),
offset: origin_cursor.unwrap(),
});
- sort_results(&mut expected);
- sort_results(&mut actual);
-
assert_eq!(actual, expected);
}
-fn sort_results(items: &mut Vec<DefinitionResult>) {
- items.sort_by(|a, b| {
- let a = (&a.target.uri, a.target_range.start());
- let b = (&b.target.uri, b.target_range.start());
- a.cmp(&b)
- });
-}
-
#[test]
fn test_command_definition() {
check(
diff --git a/support/texlab/crates/diagnostics/src/tests.rs b/support/texlab/crates/diagnostics/src/tests.rs
index 8b9534f337..b9eafc0284 100644
--- a/support/texlab/crates/diagnostics/src/tests.rs
+++ b/support/texlab/crates/diagnostics/src/tests.rs
@@ -118,7 +118,7 @@ fn test_tex_unmatched_braces() {
%! main.tex
}
^
-{
+{
!
"#,
&[
diff --git a/support/texlab/crates/hover/src/entry_type.rs b/support/texlab/crates/hover/src/entry_type.rs
index c28c88b611..f4941b4ee0 100644
--- a/support/texlab/crates/hover/src/entry_type.rs
+++ b/support/texlab/crates/hover/src/entry_type.rs
@@ -10,7 +10,7 @@ pub(super) fn find_hover<'db>(params: &'db HoverParams) -> Option<Hover<'db>> {
.token_at_offset(params.offset)
.find(|x| x.kind() == bibtex::TYPE)?;
- let entry_type = BibtexEntryType::find(&name.text()[1..])?;
+ let entry_type = BibtexEntryType::find(name.text())?;
Some(Hover {
range: name.text_range(),
data: HoverData::EntryType(entry_type),
diff --git a/support/texlab/crates/hover/src/tests.rs b/support/texlab/crates/hover/src/tests.rs
index 2890af938f..0db913d0b5 100644
--- a/support/texlab/crates/hover/src/tests.rs
+++ b/support/texlab/crates/hover/src/tests.rs
@@ -98,7 +98,7 @@ fn test_bibtex_entry_type_known() {
Some(
EntryType(
BibtexEntryType {
- name: "article",
+ name: "@article",
category: Article,
documentation: Some(
"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`",
diff --git a/support/texlab/crates/parser/Cargo.toml b/support/texlab/crates/parser/Cargo.toml
index 6df7596933..989569d0b6 100644
--- a/support/texlab/crates/parser/Cargo.toml
+++ b/support/texlab/crates/parser/Cargo.toml
@@ -15,7 +15,7 @@ rustc-hash = "1.1.0"
syntax = { path = "../syntax" }
[dev-dependencies]
-insta = { version = "1.31.0", features = ["glob", "redactions", "json"] }
+insta = { version = "1.32.0", features = ["glob", "redactions", "json"] }
[lib]
doctest = false
diff --git a/support/texlab/crates/parser/src/latex.rs b/support/texlab/crates/parser/src/latex.rs
index 3c9cdf8665..f96d41a722 100644
--- a/support/texlab/crates/parser/src/latex.rs
+++ b/support/texlab/crates/parser/src/latex.rs
@@ -714,6 +714,7 @@ impl<'a> Parser<'a> {
| Token::Comma
| Token::LBrack
| Token::RBrack
+ | Token::Dollar
| Token::CommandName(CommandName::Generic) => self.path(),
Token::LCurly => self.curly_group_path(),
Token::Whitespace | Token::Pipe => self.eat(),
@@ -738,6 +739,7 @@ impl<'a> Parser<'a> {
| Token::RBrack
| Token::LParen
| Token::RParen
+ | Token::Dollar
| Token::CommandName(CommandName::Generic) => self.path(),
Token::Whitespace
| Token::LineBreak
@@ -767,6 +769,7 @@ impl<'a> Parser<'a> {
| Token::RBrack
| Token::LParen
| Token::RParen
+ | Token::Dollar
| Token::CommandName(CommandName::Generic) => self.eat(),
Token::LCurly => self.curly_group_path(),
_ => break,
diff --git a/support/texlab/crates/parser/src/latex/lexer/types.rs b/support/texlab/crates/parser/src/latex/lexer/types.rs
index 607138a448..7ce079d23e 100644
--- a/support/texlab/crates/parser/src/latex/lexer/types.rs
+++ b/support/texlab/crates/parser/src/latex/lexer/types.rs
@@ -52,7 +52,14 @@ fn lex_command_name(lexer: &mut logos::Lexer<Token>) -> CommandName {
let input = &lexer.source()[lexer.span().end..];
let mut chars = input.chars();
- let Some(c) = chars.next() else { return CommandName::Generic };
+ let Some(c) = chars.next() else {
+ return CommandName::Generic;
+ };
+
+ if c.is_whitespace() {
+ return CommandName::Generic;
+ }
+
lexer.bump(c.len_utf8());
if !c.is_alphanumeric() && c != '@' {
return CommandName::Generic;
diff --git a/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_931.txt.snap b/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_931.txt.snap
new file mode 100644
index 0000000000..2fef140d95
--- /dev/null
+++ b/support/texlab/crates/parser/src/snapshots/parser__latex__tests__parse@issue_931.txt.snap
@@ -0,0 +1,16 @@
+---
+source: crates/parser/src/latex.rs
+expression: root
+input_file: crates/parser/src/test_data/latex/issue_931.txt
+---
+ROOT@0..31
+ PREAMBLE@0..31
+ BIBTEX_INCLUDE@0..31
+ COMMAND_NAME@0..13 "\\bibliography"
+ CURLY_GROUP_WORD_LIST@13..31
+ L_CURLY@13..14 "{"
+ KEY@14..30
+ DOLLAR@14..15 "$"
+ WORD@15..30 "HOME/Literature"
+ R_CURLY@30..31 "}"
+
diff --git a/support/texlab/crates/parser/src/test_data/latex/issue_931.txt b/support/texlab/crates/parser/src/test_data/latex/issue_931.txt
new file mode 100644
index 0000000000..8d75cecfae
--- /dev/null
+++ b/support/texlab/crates/parser/src/test_data/latex/issue_931.txt
@@ -0,0 +1 @@
+\bibliography{$HOME/Literature} \ No newline at end of file
diff --git a/support/texlab/crates/rename/Cargo.toml b/support/texlab/crates/rename/Cargo.toml
new file mode 100644
index 0000000000..90cf4f4197
--- /dev/null
+++ b/support/texlab/crates/rename/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "rename"
+version = "0.0.0"
+license.workspace = true
+authors.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+base-db = { path = "../base-db" }
+rowan = "0.15.11"
+rustc-hash = "1.1.0"
+syntax = { path = "../syntax" }
+
+[dev-dependencies]
+test-utils = { path = "../test-utils" }
+
+[lib]
+doctest = false
diff --git a/support/texlab/crates/rename/src/command.rs b/support/texlab/crates/rename/src/command.rs
new file mode 100644
index 0000000000..7ba0d49f0b
--- /dev/null
+++ b/support/texlab/crates/rename/src/command.rs
@@ -0,0 +1,37 @@
+use base_db::{semantics::Span, DocumentData};
+use rowan::TextRange;
+use syntax::latex;
+
+use crate::{RenameBuilder, RenameParams};
+
+pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> {
+ let data = params.inner.document.data.as_tex()?;
+ let token = data
+ .root_node()
+ .token_at_offset(params.offset)
+ .find(|token| token.kind() == latex::COMMAND_NAME)?;
+
+ Some(Span::from(&token))
+}
+
+pub(super) fn rename<'a>(builder: &mut RenameBuilder) -> Option<()> {
+ let name = prepare_rename(&builder.params)?;
+
+ for document in &builder.params.inner.project.documents {
+ let DocumentData::Tex(data) = &document.data else {
+ continue;
+ };
+
+ let mut edits = Vec::new();
+ for command in &data.semantics.commands {
+ if command.text == &name.text[1..] {
+ let range = TextRange::new(command.range.start(), command.range.end());
+ edits.push(range);
+ }
+ }
+
+ builder.result.changes.insert(*document, edits);
+ }
+
+ Some(())
+}
diff --git a/support/texlab/crates/rename/src/entry.rs b/support/texlab/crates/rename/src/entry.rs
new file mode 100644
index 0000000000..11800f1371
--- /dev/null
+++ b/support/texlab/crates/rename/src/entry.rs
@@ -0,0 +1,49 @@
+use base_db::{
+ semantics::{bib, tex, Span},
+ util::queries::{self},
+ DocumentData,
+};
+
+use crate::{RenameBuilder, RenameParams};
+
+pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> {
+ match &params.inner.document.data {
+ DocumentData::Tex(data) => {
+ let result = queries::object_at_cursor(
+ &data.semantics.citations,
+ params.offset,
+ queries::SearchMode::Name,
+ )?;
+
+ Some(Span::new(result.object.name.text.clone(), result.range))
+ }
+ DocumentData::Bib(data) => {
+ let result = queries::object_at_cursor(
+ &data.semantics.entries,
+ params.offset,
+ queries::SearchMode::Name,
+ )?;
+
+ Some(Span::new(result.object.name.text.clone(), result.range))
+ }
+ _ => None,
+ }
+}
+
+pub(super) fn rename<'a>(builder: &mut RenameBuilder) -> Option<()> {
+ let name = prepare_rename(&builder.params)?;
+
+ let project = &builder.params.inner.project;
+ let citations = queries::objects_with_name::<tex::Citation>(project, &name.text)
+ .map(|(doc, obj)| (doc, obj.name.range));
+
+ let entries = queries::objects_with_name::<bib::Entry>(project, &name.text)
+ .map(|(doc, obj)| (doc, obj.name.range));
+
+ for (document, range) in citations.chain(entries) {
+ let entry = builder.result.changes.entry(document);
+ entry.or_default().push(range);
+ }
+
+ Some(())
+}
diff --git a/support/texlab/crates/rename/src/label.rs b/support/texlab/crates/rename/src/label.rs
new file mode 100644
index 0000000000..983cc4f912
--- /dev/null
+++ b/support/texlab/crates/rename/src/label.rs
@@ -0,0 +1,25 @@
+use base_db::{
+ semantics::{tex, Span},
+ util::queries::{self, Object},
+};
+
+use crate::{RenameBuilder, RenameParams};
+
+pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> {
+ let data = params.inner.document.data.as_tex()?;
+ let labels = &data.semantics.labels;
+ let label = queries::object_at_cursor(labels, params.offset, queries::SearchMode::Name)?;
+ Some(Span::new(label.object.name.text.clone(), label.range))
+}
+
+pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> {
+ let name = prepare_rename(&builder.params)?;
+
+ let project = &builder.params.inner.project;
+ for (document, label) in queries::objects_with_name::<tex::Label>(project, &name.text) {
+ let entry = builder.result.changes.entry(document);
+ entry.or_default().push(label.name_range());
+ }
+
+ Some(())
+}
diff --git a/support/texlab/crates/rename/src/lib.rs b/support/texlab/crates/rename/src/lib.rs
new file mode 100644
index 0000000000..d9b28c3586
--- /dev/null
+++ b/support/texlab/crates/rename/src/lib.rs
@@ -0,0 +1,44 @@
+mod command;
+mod entry;
+mod label;
+
+use base_db::{Document, FeatureParams};
+use rowan::{TextRange, TextSize};
+use rustc_hash::FxHashMap;
+
+#[derive(Debug)]
+pub struct RenameParams<'db> {
+ pub inner: FeatureParams<'db>,
+ pub offset: TextSize,
+}
+
+#[derive(Debug, Default)]
+pub struct RenameResult<'db> {
+ pub changes: FxHashMap<&'db Document, Vec<TextRange>>,
+}
+
+struct RenameBuilder<'db> {
+ params: &'db RenameParams<'db>,
+ result: RenameResult<'db>,
+}
+
+pub fn prepare_rename(params: &RenameParams) -> Option<TextRange> {
+ command::prepare_rename(&params)
+ .or_else(|| entry::prepare_rename(&params))
+ .or_else(|| label::prepare_rename(&params))
+ .map(|span| span.range)
+}
+
+pub fn rename<'db>(params: &'db RenameParams<'db>) -> RenameResult<'db> {
+ let result = RenameResult::default();
+ let mut builder = RenameBuilder { params, result };
+
+ command::rename(&mut builder)
+ .or_else(|| entry::rename(&mut builder))
+ .or_else(|| label::rename(&mut builder));
+
+ builder.result
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/support/texlab/crates/rename/src/tests.rs b/support/texlab/crates/rename/src/tests.rs
new file mode 100644
index 0000000000..d931e92798
--- /dev/null
+++ b/support/texlab/crates/rename/src/tests.rs
@@ -0,0 +1,97 @@
+use base_db::FeatureParams;
+use rustc_hash::FxHashMap;
+
+use crate::RenameParams;
+
+fn check(input: &str) {
+ let fixture = test_utils::fixture::Fixture::parse(input);
+
+ let mut expected = FxHashMap::default();
+ for spec in &fixture.documents {
+ if !spec.ranges.is_empty() {
+ let document = fixture.workspace.lookup(&spec.uri).unwrap();
+ expected.insert(document, spec.ranges.clone());
+ }
+ }
+
+ let (document, offset) = fixture
+ .documents
+ .iter()
+ .find_map(|spec| Some((fixture.workspace.lookup(&spec.uri)?, spec.cursor?)))
+ .unwrap();
+
+ let inner = FeatureParams::new(&fixture.workspace, document);
+ let params = RenameParams { inner, offset };
+ let actual = crate::rename(&params);
+ assert_eq!(actual.changes, expected);
+}
+
+#[test]
+fn command() {
+ check(
+ r#"
+%! foo.tex
+\baz
+ |
+ ^^^
+\include{bar.tex}
+
+%! bar.tex
+\baz
+ ^^^
+"#,
+ )
+}
+
+#[test]
+fn entry() {
+ check(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+ |
+ ^^^
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}
+ ^^^
+"#,
+ )
+}
+
+#[test]
+fn citation() {
+ check(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+ ^^^
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}
+ |
+ ^^^
+"#,
+ )
+}
+
+#[test]
+fn label() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}\include{bar}
+ |
+ ^^^
+
+%! bar.tex
+\ref{foo}
+ ^^^
+
+%! baz.tex
+\ref{foo}
+"#,
+ )
+}
diff --git a/support/texlab/crates/symbols/Cargo.toml b/support/texlab/crates/symbols/Cargo.toml
index 348e28d576..19a9c44964 100644
--- a/support/texlab/crates/symbols/Cargo.toml
+++ b/support/texlab/crates/symbols/Cargo.toml
@@ -19,6 +19,6 @@ titlecase = "2.2.1"
url = "=2.3.1"
[dev-dependencies]
-insta = "1.31.0"
regex = "1.9.1"
test-utils = { path = "../test-utils" }
+expect-test = "1.4.1"
diff --git a/support/texlab/crates/symbols/src/document/bib.rs b/support/texlab/crates/symbols/src/document/bib.rs
index 1e5d934a0d..f918eb9de9 100644
--- a/support/texlab/crates/symbols/src/document/bib.rs
+++ b/support/texlab/crates/symbols/src/document/bib.rs
@@ -39,7 +39,7 @@ impl SymbolBuilder {
.filter_map(|field| self.visit_field(&field))
.collect();
- let category = BibtexEntryType::find(&ty.text()[1..])
+ let category = BibtexEntryType::find(ty.text())
.map_or(BibtexEntryTypeCategory::Misc, |ty| ty.category);
Some(Symbol {
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__allowed_patterns.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__allowed_patterns.snap
deleted file mode 100644
index e22ce2a01d..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__allowed_patterns.snap
+++ /dev/null
@@ -1,31 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Enumerate",
- kind: Enumeration,
- label: None,
- full_range: 98..159,
- selection_range: 98..159,
- children: [
- Symbol {
- name: "Item",
- kind: EnumerationItem,
- label: None,
- full_range: 120..129,
- selection_range: 120..129,
- children: [],
- },
- Symbol {
- name: "Item",
- kind: EnumerationItem,
- label: None,
- full_range: 134..143,
- selection_range: 134..143,
- children: [],
- },
- ],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__enumerate.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__enumerate.snap
deleted file mode 100644
index f94828c362..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__enumerate.snap
+++ /dev/null
@@ -1,62 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Enumerate",
- kind: Enumeration,
- label: None,
- full_range: 43..184,
- selection_range: 43..184,
- children: [
- Symbol {
- name: "1",
- kind: EnumerationItem,
- label: Some(
- Span(
- "it:foo",
- 70..84,
- ),
- ),
- full_range: 65..88,
- selection_range: 70..84,
- children: [],
- },
- Symbol {
- name: "Item",
- kind: EnumerationItem,
- label: Some(
- Span(
- "it:bar",
- 98..112,
- ),
- ),
- full_range: 93..116,
- selection_range: 98..112,
- children: [],
- },
- Symbol {
- name: "Baz",
- kind: EnumerationItem,
- label: None,
- full_range: 121..135,
- selection_range: 121..135,
- children: [],
- },
- Symbol {
- name: "2",
- kind: EnumerationItem,
- label: Some(
- Span(
- "it:qux",
- 150..164,
- ),
- ),
- full_range: 140..168,
- selection_range: 150..164,
- children: [],
- },
- ],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__equation.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__equation.snap
deleted file mode 100644
index 0e284c1d71..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__equation.snap
+++ /dev/null
@@ -1,40 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Equation (1)",
- kind: Equation,
- label: Some(
- Span(
- "eq:foo",
- 59..73,
- ),
- ),
- full_range: 43..96,
- selection_range: 59..73,
- children: [],
- },
- Symbol {
- name: "Equation",
- kind: Equation,
- label: Some(
- Span(
- "eq:bar",
- 114..128,
- ),
- ),
- full_range: 98..151,
- selection_range: 114..128,
- children: [],
- },
- Symbol {
- name: "Equation",
- kind: Equation,
- label: None,
- full_range: 153..192,
- selection_range: 153..192,
- children: [],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__float.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__float.snap
deleted file mode 100644
index e9bd711c40..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__float.snap
+++ /dev/null
@@ -1,40 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Figure 1: Foo",
- kind: Figure,
- label: Some(
- Span(
- "fig:foo",
- 83..98,
- ),
- ),
- full_range: 43..111,
- selection_range: 83..98,
- children: [],
- },
- Symbol {
- name: "Figure: Bar",
- kind: Figure,
- label: Some(
- Span(
- "fig:bar",
- 153..168,
- ),
- ),
- full_range: 113..181,
- selection_range: 153..168,
- children: [],
- },
- Symbol {
- name: "Figure: Baz",
- kind: Figure,
- label: None,
- full_range: 183..236,
- selection_range: 183..236,
- children: [],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__ignored_patterns.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__ignored_patterns.snap
deleted file mode 100644
index 044e2ca531..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__ignored_patterns.snap
+++ /dev/null
@@ -1,19 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Equation",
- kind: Equation,
- label: Some(
- Span(
- "eq:foo",
- 59..73,
- ),
- ),
- full_range: 43..96,
- selection_range: 59..73,
- children: [],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__section.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__section.snap
deleted file mode 100644
index 6cd44939cf..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__section.snap
+++ /dev/null
@@ -1,41 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Foo",
- kind: Section,
- label: None,
- full_range: 43..56,
- selection_range: 43..56,
- children: [],
- },
- Symbol {
- name: "2 Bar",
- kind: Section,
- label: Some(
- Span(
- "sec:bar",
- 71..86,
- ),
- ),
- full_range: 58..119,
- selection_range: 71..86,
- children: [
- Symbol {
- name: "Baz",
- kind: Section,
- label: Some(
- Span(
- "sec:baz",
- 104..119,
- ),
- ),
- full_range: 88..119,
- selection_range: 104..119,
- children: [],
- },
- ],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_amsthm.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_amsthm.snap
deleted file mode 100644
index 9d897532b1..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_amsthm.snap
+++ /dev/null
@@ -1,53 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Lemma 1 (Foo)",
- kind: Theorem,
- label: Some(
- Span(
- "thm:foo",
- 107..122,
- ),
- ),
- full_range: 89..142,
- selection_range: 107..122,
- children: [],
- },
- Symbol {
- name: "Lemma 2",
- kind: Theorem,
- label: Some(
- Span(
- "thm:bar",
- 157..172,
- ),
- ),
- full_range: 144..192,
- selection_range: 157..172,
- children: [],
- },
- Symbol {
- name: "Lemma",
- kind: Theorem,
- label: Some(
- Span(
- "thm:baz",
- 207..222,
- ),
- ),
- full_range: 194..242,
- selection_range: 207..222,
- children: [],
- },
- Symbol {
- name: "Lemma (Qux)",
- kind: Theorem,
- label: None,
- full_range: 244..282,
- selection_range: 244..282,
- children: [],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_thmtools.snap b/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_thmtools.snap
deleted file mode 100644
index cb5d344965..0000000000
--- a/support/texlab/crates/symbols/src/document/snapshots/symbols__document__tests__theorem_thmtools.snap
+++ /dev/null
@@ -1,53 +0,0 @@
----
-source: crates/symbols/src/document/tests.rs
-expression: "document_symbols(&fixture.workspace, document)"
----
-[
- Symbol {
- name: "Lemma 1 (Foo)",
- kind: Theorem,
- label: Some(
- Span(
- "thm:foo",
- 147..162,
- ),
- ),
- full_range: 129..182,
- selection_range: 147..162,
- children: [],
- },
- Symbol {
- name: "Lemma 2",
- kind: Theorem,
- label: Some(
- Span(
- "thm:bar",
- 197..212,
- ),
- ),
- full_range: 184..232,
- selection_range: 197..212,
- children: [],
- },
- Symbol {
- name: "Lemma",
- kind: Theorem,
- label: Some(
- Span(
- "thm:baz",
- 247..262,
- ),
- ),
- full_range: 234..282,
- selection_range: 247..262,
- children: [],
- },
- Symbol {
- name: "Lemma (Qux)",
- kind: Theorem,
- label: None,
- full_range: 284..322,
- selection_range: 284..322,
- children: [],
- },
-]
diff --git a/support/texlab/crates/symbols/src/document/tests.rs b/support/texlab/crates/symbols/src/document/tests.rs
index 355a8a5719..25450a6cc5 100644
--- a/support/texlab/crates/symbols/src/document/tests.rs
+++ b/support/texlab/crates/symbols/src/document/tests.rs
@@ -1,10 +1,16 @@
use base_db::{Config, SymbolConfig};
-use insta::assert_debug_snapshot;
+use expect_test::{expect, Expect};
use regex::Regex;
use test_utils::fixture::Fixture;
use crate::document_symbols;
+fn check(fixture: &Fixture, expect: Expect) {
+ let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
+ let symbols = document_symbols(&fixture.workspace, document);
+ expect.assert_debug_eq(&symbols);
+}
+
#[test]
fn test_enumerate() {
let fixture = Fixture::parse(
@@ -29,8 +35,69 @@ fn test_enumerate() {
\newlabel{it:qux}{{2}{1}}"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Enumerate",
+ kind: Enumeration,
+ label: None,
+ full_range: 43..184,
+ selection_range: 43..184,
+ children: [
+ Symbol {
+ name: "1",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "it:foo",
+ 70..84,
+ ),
+ ),
+ full_range: 65..88,
+ selection_range: 70..84,
+ children: [],
+ },
+ Symbol {
+ name: "Item",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "it:bar",
+ 98..112,
+ ),
+ ),
+ full_range: 93..116,
+ selection_range: 98..112,
+ children: [],
+ },
+ Symbol {
+ name: "Baz",
+ kind: EnumerationItem,
+ label: None,
+ full_range: 121..135,
+ selection_range: 121..135,
+ children: [],
+ },
+ Symbol {
+ name: "2",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "it:qux",
+ 150..164,
+ ),
+ ),
+ full_range: 140..168,
+ selection_range: 150..164,
+ children: [],
+ },
+ ],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -61,8 +128,47 @@ fn test_equation() {
\newlabel{eq:foo}{{1}{1}}"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Equation (1)",
+ kind: Equation,
+ label: Some(
+ Span(
+ "eq:foo",
+ 59..73,
+ ),
+ ),
+ full_range: 43..96,
+ selection_range: 59..73,
+ children: [],
+ },
+ Symbol {
+ name: "Equation",
+ kind: Equation,
+ label: Some(
+ Span(
+ "eq:bar",
+ 114..128,
+ ),
+ ),
+ full_range: 98..151,
+ selection_range: 114..128,
+ children: [],
+ },
+ Symbol {
+ name: "Equation",
+ kind: Equation,
+ label: None,
+ full_range: 153..192,
+ selection_range: 153..192,
+ children: [],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -104,8 +210,47 @@ fn test_float() {
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Baz}}{1}\protected@file@percent }"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Figure 1: Foo",
+ kind: Figure,
+ label: Some(
+ Span(
+ "fig:foo",
+ 83..98,
+ ),
+ ),
+ full_range: 43..111,
+ selection_range: 83..98,
+ children: [],
+ },
+ Symbol {
+ name: "Figure: Bar",
+ kind: Figure,
+ label: Some(
+ Span(
+ "fig:bar",
+ 153..168,
+ ),
+ ),
+ full_range: 113..181,
+ selection_range: 153..168,
+ children: [],
+ },
+ Symbol {
+ name: "Figure: Baz",
+ kind: Figure,
+ label: None,
+ full_range: 183..236,
+ selection_range: 183..236,
+ children: [],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -133,8 +278,48 @@ fn test_section() {
\newlabel{sec:bar}{{2}{1}}"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Foo",
+ kind: Section,
+ label: None,
+ full_range: 43..56,
+ selection_range: 43..56,
+ children: [],
+ },
+ Symbol {
+ name: "2 Bar",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:bar",
+ 71..86,
+ ),
+ ),
+ full_range: 58..119,
+ selection_range: 71..86,
+ children: [
+ Symbol {
+ name: "Baz",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:baz",
+ 104..119,
+ ),
+ ),
+ full_range: 88..119,
+ selection_range: 104..119,
+ children: [],
+ },
+ ],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -173,8 +358,60 @@ fn test_theorem_amsthm() {
\newlabel{thm:bar}{{2}{1}}"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Lemma 1 (Foo)",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:foo",
+ 107..122,
+ ),
+ ),
+ full_range: 89..142,
+ selection_range: 107..122,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma 2",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:bar",
+ 157..172,
+ ),
+ ),
+ full_range: 144..192,
+ selection_range: 157..172,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:baz",
+ 207..222,
+ ),
+ ),
+ full_range: 194..242,
+ selection_range: 207..222,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma (Qux)",
+ kind: Theorem,
+ label: None,
+ full_range: 244..282,
+ selection_range: 244..282,
+ children: [],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -213,8 +450,60 @@ fn test_theorem_thmtools() {
\newlabel{thm:bar}{{2}{1}}"#,
);
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Lemma 1 (Foo)",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:foo",
+ 147..162,
+ ),
+ ),
+ full_range: 129..182,
+ selection_range: 147..162,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma 2",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:bar",
+ 197..212,
+ ),
+ ),
+ full_range: 184..232,
+ selection_range: 197..212,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:baz",
+ 247..262,
+ ),
+ ),
+ full_range: 234..282,
+ selection_range: 247..262,
+ children: [],
+ },
+ Symbol {
+ name: "Lemma (Qux)",
+ kind: Theorem,
+ label: None,
+ full_range: 284..322,
+ selection_range: 284..322,
+ children: [],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -249,8 +538,38 @@ fn test_allowed_patterns() {
..Config::default()
});
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Enumerate",
+ kind: Enumeration,
+ label: None,
+ full_range: 98..159,
+ selection_range: 98..159,
+ children: [
+ Symbol {
+ name: "Item",
+ kind: EnumerationItem,
+ label: None,
+ full_range: 120..129,
+ selection_range: 120..129,
+ children: [],
+ },
+ Symbol {
+ name: "Item",
+ kind: EnumerationItem,
+ label: None,
+ full_range: 134..143,
+ selection_range: 134..143,
+ children: [],
+ },
+ ],
+ },
+ ]
+ "#]],
+ );
}
#[test]
@@ -285,6 +604,24 @@ fn test_ignored_patterns() {
..Config::default()
});
- let document = fixture.workspace.lookup(&fixture.documents[0].uri).unwrap();
- assert_debug_snapshot!(document_symbols(&fixture.workspace, document));
+ check(
+ &fixture,
+ expect![[r#"
+ [
+ Symbol {
+ name: "Equation",
+ kind: Equation,
+ label: Some(
+ Span(
+ "eq:foo",
+ 59..73,
+ ),
+ ),
+ full_range: 43..96,
+ selection_range: 59..73,
+ children: [],
+ },
+ ]
+ "#]],
+ );
}
diff --git a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_bibtex.snap b/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_bibtex.snap
deleted file mode 100644
index 5bd00182b0..0000000000
--- a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_bibtex.snap
+++ /dev/null
@@ -1,36 +0,0 @@
----
-source: crates/symbols/src/workspace/tests.rs
-expression: "workspace_symbols(&fixture.workspace, \"bibtex\")"
----
-[
- SymbolLocation {
- document: Document(
- "file:///texlab/main.bib",
- ),
- symbol: Symbol {
- name: "foo",
- kind: Entry(
- Article,
- ),
- label: None,
- full_range: 0..14,
- selection_range: 9..12,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.bib",
- ),
- symbol: Symbol {
- name: "bar",
- kind: Entry(
- String,
- ),
- label: None,
- full_range: 16..36,
- selection_range: 24..27,
- children: [],
- },
- },
-]
diff --git a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_figure.snap b/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_figure.snap
deleted file mode 100644
index 412e7c14d2..0000000000
--- a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_figure.snap
+++ /dev/null
@@ -1,24 +0,0 @@
----
-source: crates/symbols/src/workspace/tests.rs
-expression: "workspace_symbols(&fixture.workspace, \"figure\")"
----
-[
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "Figure 1: Bar",
- kind: Figure,
- label: Some(
- Span(
- "fig:bar",
- 265..280,
- ),
- ),
- full_range: 220..293,
- selection_range: 265..280,
- children: [],
- },
- },
-]
diff --git a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_item.snap b/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_item.snap
deleted file mode 100644
index 993d046d3d..0000000000
--- a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_item.snap
+++ /dev/null
@@ -1,60 +0,0 @@
----
-source: crates/symbols/src/workspace/tests.rs
-expression: "workspace_symbols(&fixture.workspace, \"item\")"
----
-[
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "1",
- kind: EnumerationItem,
- label: Some(
- Span(
- "itm:foo",
- 352..367,
- ),
- ),
- full_range: 347..371,
- selection_range: 352..367,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "2",
- kind: EnumerationItem,
- label: Some(
- Span(
- "itm:bar",
- 381..396,
- ),
- ),
- full_range: 376..400,
- selection_range: 381..396,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "3",
- kind: EnumerationItem,
- label: Some(
- Span(
- "itm:baz",
- 410..425,
- ),
- ),
- full_range: 405..429,
- selection_range: 410..425,
- children: [],
- },
- },
-]
diff --git a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_math.snap b/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_math.snap
deleted file mode 100644
index 60a002fba5..0000000000
--- a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_math.snap
+++ /dev/null
@@ -1,42 +0,0 @@
----
-source: crates/symbols/src/workspace/tests.rs
-expression: "workspace_symbols(&fixture.workspace, \"math\")"
----
-[
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "Equation (1)",
- kind: Equation,
- label: Some(
- Span(
- "eq:foo",
- 151..165,
- ),
- ),
- full_range: 135..188,
- selection_range: 151..165,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "Lemma 1 (Qux)",
- kind: Theorem,
- label: Some(
- Span(
- "thm:qux",
- 522..537,
- ),
- ),
- full_range: 504..557,
- selection_range: 522..537,
- children: [],
- },
- },
-]
diff --git a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_section.snap b/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_section.snap
deleted file mode 100644
index 4cf0cb5fe2..0000000000
--- a/support/texlab/crates/symbols/src/workspace/snapshots/symbols__workspace__tests__filter_type_section.snap
+++ /dev/null
@@ -1,78 +0,0 @@
----
-source: crates/symbols/src/workspace/tests.rs
-expression: "workspace_symbols(&fixture.workspace, \"section\")"
----
-[
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "1 Foo",
- kind: Section,
- label: Some(
- Span(
- "sec:foo",
- 118..133,
- ),
- ),
- full_range: 105..188,
- selection_range: 118..133,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "2 Bar",
- kind: Section,
- label: Some(
- Span(
- "sec:bar",
- 203..218,
- ),
- ),
- full_range: 190..293,
- selection_range: 203..218,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "3 Baz",
- kind: Section,
- label: Some(
- Span(
- "sec:baz",
- 308..323,
- ),
- ),
- full_range: 295..445,
- selection_range: 308..323,
- children: [],
- },
- },
- SymbolLocation {
- document: Document(
- "file:///texlab/main.tex",
- ),
- symbol: Symbol {
- name: "4 Qux",
- kind: Section,
- label: Some(
- Span(
- "sec:qux",
- 460..475,
- ),
- ),
- full_range: 447..557,
- selection_range: 460..475,
- children: [],
- },
- },
-]
diff --git a/support/texlab/crates/symbols/src/workspace/tests.rs b/support/texlab/crates/symbols/src/workspace/tests.rs
index 1231be869d..26bdefa6b6 100644
--- a/support/texlab/crates/symbols/src/workspace/tests.rs
+++ b/support/texlab/crates/symbols/src/workspace/tests.rs
@@ -1,4 +1,4 @@
-use insta::assert_debug_snapshot;
+use expect_test::{expect, Expect};
use test_utils::fixture::Fixture;
use crate::workspace_symbols;
@@ -68,32 +68,273 @@ static FIXTURE: &str = r#"
@string{bar = "bar"}"#;
+fn check(query: &str, expect: Expect) {
+ let fixture = Fixture::parse(FIXTURE);
+ let symbols = workspace_symbols(&fixture.workspace, query);
+ expect.assert_debug_eq(&symbols);
+}
+
#[test]
fn filter_type_section() {
- let fixture = Fixture::parse(FIXTURE);
- assert_debug_snapshot!(workspace_symbols(&fixture.workspace, "section"));
+ check(
+ "section",
+ expect![[r#"
+ [
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "1 Foo",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:foo",
+ 118..133,
+ ),
+ ),
+ full_range: 105..188,
+ selection_range: 118..133,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "2 Bar",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:bar",
+ 203..218,
+ ),
+ ),
+ full_range: 190..293,
+ selection_range: 203..218,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "3 Baz",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:baz",
+ 308..323,
+ ),
+ ),
+ full_range: 295..445,
+ selection_range: 308..323,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "4 Qux",
+ kind: Section,
+ label: Some(
+ Span(
+ "sec:qux",
+ 460..475,
+ ),
+ ),
+ full_range: 447..557,
+ selection_range: 460..475,
+ children: [],
+ },
+ },
+ ]
+ "#]],
+ );
}
#[test]
fn filter_type_figure() {
- let fixture = Fixture::parse(FIXTURE);
- assert_debug_snapshot!(workspace_symbols(&fixture.workspace, "figure"));
+ check(
+ "figure",
+ expect![[r#"
+ [
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "Figure 1: Bar",
+ kind: Figure,
+ label: Some(
+ Span(
+ "fig:bar",
+ 265..280,
+ ),
+ ),
+ full_range: 220..293,
+ selection_range: 265..280,
+ children: [],
+ },
+ },
+ ]
+ "#]],
+ );
}
#[test]
fn filter_type_item() {
- let fixture = Fixture::parse(FIXTURE);
- assert_debug_snapshot!(workspace_symbols(&fixture.workspace, "item"));
+ check(
+ "item",
+ expect![[r#"
+ [
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "1",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "itm:foo",
+ 352..367,
+ ),
+ ),
+ full_range: 347..371,
+ selection_range: 352..367,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "2",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "itm:bar",
+ 381..396,
+ ),
+ ),
+ full_range: 376..400,
+ selection_range: 381..396,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "3",
+ kind: EnumerationItem,
+ label: Some(
+ Span(
+ "itm:baz",
+ 410..425,
+ ),
+ ),
+ full_range: 405..429,
+ selection_range: 410..425,
+ children: [],
+ },
+ },
+ ]
+ "#]],
+ );
}
#[test]
fn filter_type_math() {
- let fixture = Fixture::parse(FIXTURE);
- assert_debug_snapshot!(workspace_symbols(&fixture.workspace, "math"));
+ check(
+ "math",
+ expect![[r#"
+ [
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "Equation (1)",
+ kind: Equation,
+ label: Some(
+ Span(
+ "eq:foo",
+ 151..165,
+ ),
+ ),
+ full_range: 135..188,
+ selection_range: 151..165,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.tex",
+ ),
+ symbol: Symbol {
+ name: "Lemma 1 (Qux)",
+ kind: Theorem,
+ label: Some(
+ Span(
+ "thm:qux",
+ 522..537,
+ ),
+ ),
+ full_range: 504..557,
+ selection_range: 522..537,
+ children: [],
+ },
+ },
+ ]
+ "#]],
+ );
}
#[test]
fn filter_bibtex() {
- let fixture = Fixture::parse(FIXTURE);
- assert_debug_snapshot!(workspace_symbols(&fixture.workspace, "bibtex"));
+ check(
+ "bibtex",
+ expect![[r#"
+ [
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ symbol: Symbol {
+ name: "foo",
+ kind: Entry(
+ Article,
+ ),
+ label: None,
+ full_range: 0..14,
+ selection_range: 9..12,
+ children: [],
+ },
+ },
+ SymbolLocation {
+ document: Document(
+ "file:///texlab/main.bib",
+ ),
+ symbol: Symbol {
+ name: "bar",
+ kind: Entry(
+ String,
+ ),
+ label: None,
+ full_range: 16..36,
+ selection_range: 24..27,
+ children: [],
+ },
+ },
+ ]
+ "#]],
+ );
}
diff --git a/support/texlab/crates/test-utils/src/fixture.rs b/support/texlab/crates/test-utils/src/fixture.rs
index 371fc5d139..55ab162d28 100644
--- a/support/texlab/crates/test-utils/src/fixture.rs
+++ b/support/texlab/crates/test-utils/src/fixture.rs
@@ -1,6 +1,9 @@
use std::path::PathBuf;
-use base_db::{util::LineCol, Owner, Workspace};
+use base_db::{
+ util::{LineCol, LineIndex},
+ Owner, Workspace,
+};
use rowan::{TextRange, TextSize};
use url::Url;
@@ -68,33 +71,40 @@ impl DocumentSpec {
let mut cursor = None;
let mut text = String::new();
- let mut line_start = 0;
+ let mut line_nbr = 0;
for line in input.lines().map(|line| line.trim_end()) {
if line.chars().all(|c| matches!(c, ' ' | '^' | '|' | '!')) && !line.is_empty() {
cursor = cursor.or_else(|| {
let offset = line.find('|')?;
- Some(TextSize::from((line_start + offset) as u32))
+ Some(CharacterPosition::new(line_nbr, offset))
});
if let Some(start) = line.find('!') {
- let position = TextSize::from((line_start + start) as u32);
- ranges.push(TextRange::new(position, position));
+ let position = CharacterPosition::new(line_nbr, start);
+ ranges.push(CharacterRange::new(position, position));
}
if let Some(start) = line.find('^') {
let end = line.rfind('^').unwrap() + 1;
- ranges.push(TextRange::new(
- TextSize::from((line_start + start) as u32),
- TextSize::from((line_start + end) as u32),
- ));
+ let start = CharacterPosition::new(line_nbr, start);
+ let end = CharacterPosition::new(line_nbr, end);
+ ranges.push(CharacterRange::new(start, end));
}
} else {
- line_start = text.len();
text.push_str(line);
text.push('\n');
+ line_nbr += 1;
}
}
+ let line_index = LineIndex::new(&text);
+
+ let cursor = cursor.map(|cursor| cursor.to_offset(&text, &line_index));
+ let ranges = ranges
+ .into_iter()
+ .map(|range| range.to_offset(&text, &line_index))
+ .collect();
+
Self {
uri,
text,
@@ -103,3 +113,48 @@ impl DocumentSpec {
}
}
}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+struct CharacterPosition {
+ line: usize,
+ col: usize,
+}
+
+impl CharacterPosition {
+ fn new(line: usize, col: usize) -> Self {
+ Self { line, col }
+ }
+
+ fn to_offset(self, text: &str, line_index: &LineIndex) -> TextSize {
+ let start = line_index.offset(LineCol {
+ line: (self.line - 1) as u32,
+ col: 0,
+ });
+
+ let slice = &text[start.into()..];
+ let len = slice
+ .char_indices()
+ .nth(self.col)
+ .map_or_else(|| slice.len(), |(i, _)| i);
+
+ start + TextSize::try_from(len).unwrap()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+struct CharacterRange {
+ start: CharacterPosition,
+ end: CharacterPosition,
+}
+
+impl CharacterRange {
+ fn new(start: CharacterPosition, end: CharacterPosition) -> Self {
+ Self { start, end }
+ }
+
+ fn to_offset(self, text: &str, line_index: &LineIndex) -> TextRange {
+ let start = self.start.to_offset(text, line_index);
+ let end = self.end.to_offset(text, line_index);
+ TextRange::new(start, end)
+ }
+}
diff --git a/support/texlab/crates/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml
index a8d308b3a0..fc8e956a4e 100644
--- a/support/texlab/crates/texlab/Cargo.toml
+++ b/support/texlab/crates/texlab/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "texlab"
description = "LaTeX Language Server"
-version = "5.9.2"
+version = "5.10.0"
license.workspace = true
readme = "README.md"
authors.workspace = true
@@ -22,18 +22,18 @@ test = false
doctest = false
[dependencies]
-anyhow = "1.0.72"
+anyhow = "1.0.75"
base-db = { path = "../base-db" }
citeproc = { path = "../citeproc" }
-clap = { version = "4.3.15", features = ["derive"] }
+clap = { version = "4.4.4", features = ["derive"] }
commands = { path = "../commands" }
+completion = { path = "../completion" }
completion-data = { path = "../completion-data" }
crossbeam-channel = "0.5.8"
definition = { path = "../definition" }
diagnostics = { path = "../diagnostics" }
-dirs = "5.0.1"
distro = { path = "../distro" }
-encoding_rs = "0.8.32"
+encoding_rs = "0.8.33"
encoding_rs_io = "0.1.7"
fern = "0.6.2"
folding = { path = "../folding" }
@@ -41,31 +41,32 @@ fuzzy-matcher = { version = "0.3.7", features = ["compact"] }
hover = { path = "../hover" }
itertools = "0.11.0"
log = "0.4.19"
-lsp-server = "0.7.2"
-lsp-types = "0.94.0"
-notify = "6.0.1"
-notify-debouncer-full = "0.2.0"
+lsp-server = "0.7.4"
+lsp-types = "0.94.1"
+notify = "6.1.1"
+notify-debouncer-full = "0.3.1"
once_cell = "1.18.0"
parking_lot = "0.12.1"
parser = { path = "../parser" }
-rayon = "1.7.0"
+rayon = "1.8.0"
references = { path = "../references" }
-regex = "1.9.1"
+regex = "1.9.5"
+rename = { path = "../rename" }
rowan = "0.15.11"
rustc-hash = "1.1.0"
-serde = "1.0.171"
-serde_json = "1.0.103"
+serde = "1.0.188"
+serde_json = "1.0.107"
serde_regex = "1.1.0"
-serde_repr = "0.1.14"
+serde_repr = "0.1.16"
symbols = { path = "../symbols" }
syntax = { path = "../syntax" }
-tempfile = "3.6.0"
+tempfile = "3.8.0"
threadpool = "1.8.1"
[dev-dependencies]
assert_unordered = "0.3.5"
criterion = { version = "0.5.1" }
-insta = { version = "1.31.0", features = ["glob", "redactions", "json"] }
+insta = { version = "1.32.0", features = ["glob", "redactions", "json"] }
[[bench]]
name = "bench_main"
diff --git a/support/texlab/crates/texlab/benches/bench_main.rs b/support/texlab/crates/texlab/benches/bench_main.rs
index 47b302e313..da98e63bfb 100644
--- a/support/texlab/crates/texlab/benches/bench_main.rs
+++ b/support/texlab/crates/texlab/benches/bench_main.rs
@@ -1,7 +1,7 @@
use base_db::{util::LineCol, Owner, Workspace};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use distro::Language;
-use lsp_types::{ClientCapabilities, Position, Url};
+use lsp_types::{ClientCapabilities, CompletionParams, Position, TextDocumentPositionParams, Url};
use parser::{parse_latex, SyntaxConfig};
const CODE: &str = include_str!("../../../texlab.tex");
@@ -12,7 +12,7 @@ fn criterion_benchmark(c: &mut Criterion) {
b.iter(|| parse_latex(black_box(CODE), &config));
});
- c.bench_function("LaTeX/Completion/Command", |b| {
+ c.bench_function("LaTeX/Completion/Command_v2", |b| {
let uri = Url::parse("http://example.com/texlab.tex").unwrap();
let text = CODE.to_string();
let mut workspace = Workspace::default();
@@ -25,15 +25,18 @@ fn criterion_benchmark(c: &mut Criterion) {
);
let client_capabilities = ClientCapabilities::default();
+ let params = CompletionParams {
+ context: None,
+ text_document_position: TextDocumentPositionParams::new(
+ lsp_types::TextDocumentIdentifier { uri: uri.clone() },
+ Position::new(0, 1),
+ ),
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ };
b.iter(|| {
- texlab::features::completion::complete(
- &workspace,
- &uri,
- Position::new(0, 1),
- &client_capabilities,
- None,
- )
+ texlab::features::completion::complete(&workspace, &params, &client_capabilities, None)
});
});
}
diff --git a/support/texlab/crates/texlab/src/features/completion.rs b/support/texlab/crates/texlab/src/features/completion.rs
index ab7cc4a1b6..e02b39c2c6 100644
--- a/support/texlab/crates/texlab/src/features/completion.rs
+++ b/support/texlab/crates/texlab/src/features/completion.rs
@@ -1,58 +1,416 @@
-mod acronym_ref;
-mod argument;
-mod begin_snippet;
-pub mod builder;
-mod citation;
-mod color;
-mod color_model;
-mod component_command;
-mod component_environment;
-mod entry_type;
-mod field;
-mod glossary_ref;
-mod import;
-mod include;
-mod label;
-mod matcher;
-mod theorem;
-mod tikz_library;
-mod user_command;
-mod user_environment;
-
-use base_db::Workspace;
-use lsp_types::{ClientCapabilities, ClientInfo, CompletionList, Position, Url};
-
-use crate::{features::completion::builder::CompletionBuilder, util::cursor::CursorContext};
-
-pub const COMPLETION_LIMIT: usize = 50;
+use base_db::{
+ util::{LineIndex, RenderedObject},
+ Document, FeatureParams, Workspace,
+};
+use completion::{
+ ArgumentData, CompletionItem, CompletionItemData, CompletionParams, EntryTypeData,
+ FieldTypeData,
+};
+use lsp_types::{ClientCapabilities, ClientInfo, CompletionList};
+use serde::{Deserialize, Serialize};
+
+use crate::util::{
+ capabilities::ClientCapabilitiesExt, line_index_ext::LineIndexExt, lsp_enums::Structure,
+};
pub fn complete(
workspace: &Workspace,
- uri: &Url,
- position: Position,
+ params: &lsp_types::CompletionParams,
client_capabilities: &ClientCapabilities,
client_info: Option<&ClientInfo>,
) -> Option<CompletionList> {
- let context = CursorContext::new(workspace, uri, position, ())?;
- let mut builder = CompletionBuilder::new(&context, client_capabilities, client_info);
- log::debug!("[Completion] Cursor: {:?}", context.cursor);
- entry_type::complete(&context, &mut builder);
- field::complete(&context, &mut builder);
- argument::complete(&context, &mut builder);
- citation::complete(&context, &mut builder);
- import::complete(&context, &mut builder);
- color::complete(&context, &mut builder);
- color_model::complete(&context, &mut builder);
- acronym_ref::complete(&context, &mut builder);
- glossary_ref::complete(&context, &mut builder);
- include::complete(&context, &mut builder);
- label::complete(&context, &mut builder);
- tikz_library::complete(&context, &mut builder);
- component_environment::complete(&context, &mut builder);
- theorem::complete(&context, &mut builder);
- user_environment::complete(&context, &mut builder);
- begin_snippet::complete(&context, &mut builder);
- component_command::complete(&context, &mut builder);
- user_command::complete(&context, &mut builder);
- Some(builder.finish())
+ let document = workspace.lookup(&params.text_document_position.text_document.uri)?;
+ let feature = FeatureParams::new(workspace, document);
+ let offset = document
+ .line_index
+ .offset_lsp(params.text_document_position.position);
+
+ let params = CompletionParams { feature, offset };
+ let result = completion::complete(&params);
+
+ let mut list = CompletionList::default();
+ let item_builder = ItemBuilder::new(document, client_capabilities);
+ let always_incomplete = client_info.map_or(false, |info| info.name == "Visual Studio Code");
+ list.is_incomplete = always_incomplete || result.items.len() >= completion::LIMIT;
+ list.items = result
+ .items
+ .into_iter()
+ .enumerate()
+ .map(|(i, item)| item_builder.convert(item, i))
+ .collect();
+
+ Some(list)
+}
+
+struct ItemBuilder<'a> {
+ line_index: &'a LineIndex,
+ item_kinds: &'a [lsp_types::CompletionItemKind],
+ supports_snippets: bool,
+ supports_images: bool,
+}
+
+impl<'a> ItemBuilder<'a> {
+ pub fn new(document: &'a Document, client_capabilities: &'a ClientCapabilities) -> Self {
+ let line_index = &document.line_index;
+ let item_kinds = client_capabilities
+ .text_document
+ .as_ref()
+ .and_then(|cap| cap.completion.as_ref())
+ .and_then(|cap| cap.completion_item_kind.as_ref())
+ .and_then(|cap| cap.value_set.as_deref())
+ .unwrap_or_default();
+
+ let supports_snippets = client_capabilities.has_snippet_support();
+ let supports_images = client_capabilities.has_completion_markdown_support();
+ Self {
+ line_index,
+ item_kinds,
+ supports_snippets,
+ supports_images,
+ }
+ }
+
+ pub fn convert(&self, item: CompletionItem, index: usize) -> lsp_types::CompletionItem {
+ let mut result = lsp_types::CompletionItem::default();
+ let range = self.line_index.line_col_lsp_range(item.range);
+
+ match item.data {
+ CompletionItemData::Command(data) => {
+ self.convert_command(&mut result, range, data);
+ }
+ CompletionItemData::BeginEnvironment => {
+ self.convert_begin_environment(&mut result, range);
+ }
+ CompletionItemData::Citation(data) => {
+ self.convert_citation(&mut result, range, data);
+ }
+ CompletionItemData::Environment(data) => {
+ self.convert_environment(&mut result, range, data);
+ }
+ CompletionItemData::GlossaryEntry(data) => {
+ self.convert_glossary_entry(&mut result, range, data);
+ }
+ CompletionItemData::Label(data) => {
+ self.convert_label(&mut result, range, data);
+ }
+ CompletionItemData::Color(name) => {
+ self.convert_color(&mut result, range, name);
+ }
+ CompletionItemData::ColorModel(name) => {
+ self.convert_color_model(&mut result, range, name);
+ }
+ CompletionItemData::File(name) => {
+ self.convert_file(&mut result, range, name);
+ }
+ CompletionItemData::Directory(name) => {
+ self.convert_directory(&mut result, range, name);
+ }
+ CompletionItemData::Argument(data) => {
+ self.convert_argument(&mut result, range, data);
+ }
+ CompletionItemData::Package(name) => {
+ self.convert_package(&mut result, range, name);
+ }
+ CompletionItemData::DocumentClass(name) => {
+ self.convert_document_class(&mut result, range, name);
+ }
+ CompletionItemData::EntryType(data) => {
+ self.convert_entry_type(&mut result, range, data);
+ }
+ CompletionItemData::Field(data) => {
+ self.convert_field(&mut result, range, data);
+ }
+ CompletionItemData::TikzLibrary(name) => {
+ self.convert_tikz_library(&mut result, range, name);
+ }
+ }
+
+ if result
+ .kind
+ .is_some_and(|kind| !self.item_kinds.contains(&kind))
+ {
+ result.kind = Some(lsp_types::CompletionItemKind::TEXT);
+ }
+
+ result.sort_text = Some(format!("{:0>2}", index));
+ result.preselect = Some(item.preselect);
+ result
+ }
+
+ fn convert_command(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: completion::CommandData<'_>,
+ ) {
+ let origin = data.package.map_or_else(
+ || String::from("user-defined"),
+ |pkg| format_package_files(&pkg.file_names),
+ );
+
+ let detail = match &data.glyph {
+ Some(glyph) => format!("{glyph}, {origin}"),
+ None => origin,
+ };
+
+ result.label = data.name.into();
+ result.detail = Some(detail);
+ result.kind = Some(Structure::Command.completion_kind());
+ result.documentation = data
+ .image
+ .and_then(|base64| self.inline_image(data.name, base64));
+
+ result.text_edit = Some(lsp_types::TextEdit::new(range, data.name.into()).into());
+ }
+
+ fn convert_begin_environment(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ ) {
+ if self.supports_snippets {
+ result.kind = Some(Structure::Snippet.completion_kind());
+ result.text_edit =
+ Some(lsp_types::TextEdit::new(range, "begin{$1}\n\t\n\\end{$1}".into()).into());
+
+ result.insert_text_format = Some(lsp_types::InsertTextFormat::SNIPPET);
+ } else {
+ result.kind = Some(Structure::Command.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, "begin".to_string()).into());
+ }
+
+ result.label = "begin".into();
+ result.detail = Some(format_package_files(&[]));
+ }
+
+ fn convert_citation(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: completion::CitationData<'_>,
+ ) {
+ result.label = data.entry.name.text.clone();
+ result.kind = Some(Structure::Entry(data.entry.category).completion_kind());
+ result.filter_text = Some(data.entry.keywords.clone());
+ let text_edit = lsp_types::TextEdit::new(range, data.entry.name.text.clone());
+ result.text_edit = Some(text_edit.into());
+ let resolve_info = serde_json::to_value(ResolveInfo::Citation {
+ uri: data.document.uri.clone(),
+ key: data.entry.name.text.clone(),
+ });
+ result.data = Some(resolve_info.unwrap());
+ }
+
+ fn convert_entry_type(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: EntryTypeData<'a>,
+ ) {
+ result.label = data.0.name.into();
+ result.kind = Some(Structure::Entry(data.0.category).completion_kind());
+ result.documentation = data.0.documentation.map(|value| {
+ let kind = lsp_types::MarkupKind::Markdown;
+ lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
+ kind,
+ value: value.into(),
+ })
+ });
+
+ let text_edit = lsp_types::TextEdit::new(range, data.0.name.into());
+ result.text_edit = Some(text_edit.into());
+ }
+
+ fn convert_environment(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: completion::EnvironmentData<'_>,
+ ) {
+ result.label = data.name.into();
+ result.kind = Some(Structure::Environment.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, data.name.into()).into());
+ result.detail = Some(data.package.map_or_else(
+ || String::from("user-defined"),
+ |pkg| format_package_files(&pkg.file_names),
+ ));
+ }
+
+ fn convert_glossary_entry(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: completion::GlossaryEntryData,
+ ) {
+ result.label = data.name.clone();
+ result.kind = Some(Structure::GlossaryEntry.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, data.name).into());
+ }
+
+ fn convert_label(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: completion::LabelData<'_>,
+ ) {
+ let structure = match data.object {
+ Some(RenderedObject::Float { .. }) => Structure::Float,
+ Some(RenderedObject::Section { .. }) => Structure::Section,
+ Some(RenderedObject::Theorem { .. }) => Structure::Theorem,
+ Some(RenderedObject::Equation) => Structure::Equation,
+ Some(RenderedObject::EnumItem) => Structure::Item,
+ None => Structure::Label,
+ };
+
+ result.label = data.name.into();
+ result.kind = Some(structure.completion_kind());
+ result.detail = data.header;
+ result.filter_text = Some(data.keywords);
+ result.text_edit = Some(lsp_types::TextEdit::new(range, data.name.into()).into());
+ result.documentation = data.footer.map(|footer| {
+ let kind = lsp_types::MarkupKind::Markdown;
+ let value = footer.into();
+ lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent { kind, value })
+ });
+ }
+
+ fn convert_color(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: &str,
+ ) {
+ result.label = name.into();
+ result.kind = Some(Structure::Color.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name.into()).into());
+ }
+
+ fn convert_color_model(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: &str,
+ ) {
+ result.label = name.into();
+ result.kind = Some(Structure::ColorModel.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name.into()).into());
+ }
+
+ fn convert_file(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: String,
+ ) {
+ result.label = name.clone();
+ result.kind = Some(Structure::File.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name).into());
+ }
+
+ fn convert_directory(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: String,
+ ) {
+ result.label = name.clone();
+ result.kind = Some(Structure::Folder.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name).into());
+ }
+
+ fn convert_argument(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: ArgumentData<'_>,
+ ) {
+ result.label = data.0.name.into();
+ result.kind = Some(Structure::Argument.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, data.0.name.into()).into());
+ result.documentation = data
+ .0
+ .image
+ .and_then(|base64| self.inline_image(data.0.name, base64));
+ }
+
+ fn convert_package(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: &str,
+ ) {
+ result.label = name.into();
+ result.kind = Some(Structure::Package.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name.into()).into());
+ result.data = Some(serde_json::to_value(ResolveInfo::Package).unwrap());
+ }
+
+ fn convert_document_class(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: &str,
+ ) {
+ result.label = name.into();
+ result.kind = Some(Structure::Class.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name.into()).into());
+ result.data = Some(serde_json::to_value(ResolveInfo::DocumentClass).unwrap());
+ }
+
+ fn convert_field(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ data: FieldTypeData<'a>,
+ ) {
+ result.label = data.0.name.into();
+ result.kind = Some(Structure::Field.completion_kind());
+ result.documentation = Some(lsp_types::Documentation::MarkupContent(
+ lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: data.0.documentation.into(),
+ },
+ ));
+
+ let text_edit = lsp_types::TextEdit::new(range, data.0.name.into());
+ result.text_edit = Some(text_edit.into());
+ }
+
+ fn convert_tikz_library(
+ &self,
+ result: &mut lsp_types::CompletionItem,
+ range: lsp_types::Range,
+ name: &str,
+ ) {
+ result.label = name.into();
+ result.kind = Some(Structure::TikzLibrary.completion_kind());
+ result.text_edit = Some(lsp_types::TextEdit::new(range, name.into()).into());
+ }
+
+ fn inline_image(&self, name: &str, base64: &str) -> Option<lsp_types::Documentation> {
+ if self.supports_images {
+ let kind = lsp_types::MarkupKind::Markdown;
+ let value = format!("![{name}](data:image/png;base64,{base64}|width=48,height=48)");
+ let content = lsp_types::MarkupContent { kind, value };
+ Some(lsp_types::Documentation::MarkupContent(content))
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum ResolveInfo {
+ Citation { uri: lsp_types::Url, key: String },
+ Package,
+ DocumentClass,
+}
+
+fn format_package_files(file_names: &[&str]) -> String {
+ if file_names.is_empty() {
+ "built-in".into()
+ } else {
+ file_names.join(", ")
+ }
}
diff --git a/support/texlab/crates/texlab/src/features/completion/acronym_ref.rs b/support/texlab/crates/texlab/src/features/completion/acronym_ref.rs
deleted file mode 100644
index 3337ed542e..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/acronym_ref.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use base_db::DocumentData;
-use rowan::ast::AstNode;
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let (_, range, group) = context.find_curly_group_word()?;
- latex::AcronymReference::cast(group.syntax().parent()?)?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
-
- for name in data
- .root_node()
- .descendants()
- .filter_map(latex::AcronymDefinition::cast)
- .filter_map(|node| node.name())
- .filter_map(|name| name.key())
- {
- builder.glossary_entry(range, name.to_string());
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/argument.rs b/support/texlab/crates/texlab/src/features/completion/argument.rs
deleted file mode 100644
index 842ce7cab7..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/argument.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use rowan::{ast::AstNode, TextRange};
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'a>(context: &'a CursorContext, builder: &mut CompletionBuilder<'a>) -> Option<()> {
- let token = context.cursor.as_tex()?;
-
- let range = if token.kind() == latex::WORD {
- token.text_range()
- } else {
- TextRange::empty(context.offset)
- };
-
- 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()?)?;
-
- let index = command
- .syntax()
- .children()
- .filter_map(latex::CurlyGroup::cast)
- .position(|g| g.syntax().text_range() == group.syntax().text_range())?;
-
- let command_name = command.name()?;
- let command_name = &command_name.text()[1..];
-
- for package in context.included_packages() {
- for package_command in package
- .commands
- .iter()
- .filter(|command| command.name == command_name)
- {
- for (_, param) in package_command
- .parameters
- .iter()
- .enumerate()
- .filter(|(i, _)| *i == index)
- {
- for arg in &param.0 {
- builder.generic_argument(range, arg.name, arg.image);
- }
- }
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/begin_snippet.rs b/support/texlab/crates/texlab/src/features/completion/begin_snippet.rs
deleted file mode 100644
index 06be4faf13..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/begin_snippet.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete(context: &CursorContext, builder: &mut CompletionBuilder) -> Option<()> {
- let range = context.cursor.command_range(context.offset)?;
- builder.begin_snippet(range);
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/builder.rs b/support/texlab/crates/texlab/src/features/completion/builder.rs
deleted file mode 100644
index 8457c276fe..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/builder.rs
+++ /dev/null
@@ -1,784 +0,0 @@
-use base_db::{
- data::{BibtexEntryType, BibtexEntryTypeCategory, BibtexFieldType},
- semantics::bib,
- Document, MatchingAlgo,
-};
-use fuzzy_matcher::skim::SkimMatcherV2;
-use itertools::Itertools;
-use lsp_types::{
- ClientCapabilities, ClientInfo, CompletionItem, CompletionItemKind, CompletionList,
- CompletionTextEdit, Documentation, InsertTextFormat, MarkupContent, MarkupKind, TextEdit, Url,
-};
-use rayon::prelude::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
-use rowan::{ast::AstNode, TextRange, TextSize};
-use serde::{Deserialize, Serialize};
-use syntax::{bibtex, latex};
-
-use crate::util::{
- capabilities::ClientCapabilitiesExt,
- cursor::{Cursor, CursorContext},
- line_index_ext::LineIndexExt,
- lsp_enums::Structure,
-};
-
-use super::{
- matcher::{self, Matcher},
- COMPLETION_LIMIT,
-};
-
-pub struct CompletionBuilder<'a> {
- context: &'a CursorContext<'a>,
- items: Vec<Item<'a>>,
- matcher: Box<dyn Matcher>,
- text_pattern: String,
- file_pattern: String,
- preselect: Option<String>,
- snippets: bool,
- markdown: bool,
- item_kinds: &'a [CompletionItemKind],
- always_incomplete: bool,
-}
-
-impl<'a> CompletionBuilder<'a> {
- pub fn new(
- context: &'a CursorContext,
- client_capabilities: &'a ClientCapabilities,
- client_info: Option<&'a ClientInfo>,
- ) -> Self {
- let items = Vec::new();
- let matcher: Box<dyn Matcher> = match context.workspace.config().completion.matcher {
- MatchingAlgo::Skim => Box::<SkimMatcherV2>::default(),
- MatchingAlgo::SkimIgnoreCase => Box::new(SkimMatcherV2::default().ignore_case()),
- MatchingAlgo::Prefix => Box::new(matcher::Prefix),
- MatchingAlgo::PrefixIgnoreCase => Box::new(matcher::PrefixIgnoreCase),
- };
-
- let text_pattern = match &context.cursor {
- Cursor::Tex(token) if token.kind() == latex::COMMAND_NAME => {
- if token.text_range().start() + TextSize::from(1) == context.offset {
- // Handle cases similar to this one correctly:
- // $\|$ % (| is the cursor)
- String::from("\\")
- } else {
- token.text().trim_end().into()
- }
- }
- Cursor::Tex(token) if token.kind() == latex::WORD => {
- match token.parent().and_then(latex::Key::cast) {
- Some(key) => key
- .words()
- .take_while(|word| word.text_range() != token.text_range())
- .chain(std::iter::once(token.clone()))
- .filter(|word| word.text_range().start() < context.offset)
- .join(" "),
- None => token.text().into(),
- }
- }
- Cursor::Bib(token)
- if matches!(
- token.kind(),
- bibtex::TYPE
- | bibtex::NAME
- | bibtex::WORD
- | bibtex::COMMAND_NAME
- | bibtex::ACCENT_NAME
- ) =>
- {
- token.text().into()
- }
- Cursor::Tex(_) | Cursor::Bib(_) | Cursor::Nothing => "".into(),
- };
-
- let file_pattern = text_pattern.split('/').last().unwrap().to_string();
-
- let preselect = context
- .cursor
- .as_tex()
- .and_then(|name| name.parent())
- .and_then(latex::CurlyGroupWord::cast)
- .and_then(|group| group.syntax().parent())
- .and_then(|end| end.parent())
- .and_then(latex::Environment::cast)
- .and_then(|env| env.begin())
- .and_then(|begin| begin.name())
- .and_then(|name| name.key())
- .map(|name| name.to_string());
-
- let snippets = client_capabilities.has_snippet_support();
- let markdown = client_capabilities.has_completion_markdown_support();
- let item_kinds = client_capabilities
- .text_document
- .as_ref()
- .and_then(|cap| cap.completion.as_ref())
- .and_then(|cap| cap.completion_item_kind.as_ref())
- .and_then(|cap| cap.value_set.as_deref())
- .unwrap_or_default();
-
- let always_incomplete = client_info.map_or(false, |info| info.name == "Visual Studio Code");
-
- Self {
- context,
- items,
- matcher,
- text_pattern,
- file_pattern,
- preselect,
- snippets,
- markdown,
- item_kinds,
- always_incomplete,
- }
- }
-
- pub fn glossary_entry(&mut self, range: TextRange, name: String) -> Option<()> {
- let score = self.matcher.score(&name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::GlossaryEntry { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn generic_argument(
- &mut self,
- range: TextRange,
- name: &'a str,
- image: Option<&'a str>,
- ) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Argument { name, image },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn begin_snippet(&mut self, range: TextRange) -> Option<()> {
- if self.snippets {
- let score = self.matcher.score("begin", &self.text_pattern[1..])?;
- self.items.push(Item {
- range,
- data: Data::BeginSnippet,
- preselect: false,
- score,
- });
- }
-
- Some(())
- }
-
- pub fn citations(&mut self, range: TextRange, document: &'a Document) {
- let Some(data) = document.data.as_bib() else {
- return;
- };
-
- let iter = data
- .semantics
- .entries
- .par_iter()
- .filter_map(|entry| {
- let score = self.matcher.score(&entry.keywords, &self.text_pattern)?;
- Some((entry, score))
- })
- .map(|(entry, score)| {
- let data = Data::Citation {
- document,
- key: &entry.name.text,
- filter_text: &entry.keywords,
- category: entry.category,
- };
-
- Item {
- range,
- data,
- preselect: false,
- score,
- }
- });
-
- self.items.par_extend(iter);
- }
-
- pub fn citation(
- &mut self,
- range: TextRange,
- document: &'a Document,
- entry: &'a bib::Entry,
- ) -> Option<()> {
- let score = self.matcher.score(&entry.keywords, &self.text_pattern)?;
-
- let data = Data::Citation {
- document,
- key: &entry.name.text,
- filter_text: &entry.keywords,
- category: entry.category,
- };
-
- self.items.push(Item {
- range,
- data,
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn color_model(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::ColorModel { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn color(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Color { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn component_command(
- &mut self,
- range: TextRange,
- name: &'a str,
- image: Option<&'a str>,
- glyph: Option<&'a str>,
- file_names: &'a [&'a str],
- ) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern[1..])?;
- let data = Data::ComponentCommand {
- name,
- image,
- glyph,
- file_names,
- };
-
- self.items.push(Item {
- range,
- data,
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn component_environment(
- &mut self,
- range: TextRange,
- name: &'a str,
- file_names: &'a [&'a str],
- ) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::ComponentEnvironment { name, file_names },
- preselect: Some(name) == self.preselect.as_deref(),
- score,
- });
-
- Some(())
- }
-
- pub fn entry_type(
- &mut self,
- range: TextRange,
- entry_type: &'a BibtexEntryType<'a>,
- ) -> Option<()> {
- let score = self
- .matcher
- .score(entry_type.name, &self.text_pattern[1..])?;
-
- self.items.push(Item {
- range,
- data: Data::EntryType { entry_type },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn field(&mut self, range: TextRange, field: &'a BibtexFieldType<'a>) -> Option<()> {
- let score = self.matcher.score(field.name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Field { field },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn class(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Class { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn package(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Package { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn file(&mut self, range: TextRange, name: String) -> Option<()> {
- let score = self.matcher.score(&name, &self.file_pattern)?;
- self.items.push(Item {
- range,
- data: Data::File { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn directory(&mut self, range: TextRange, name: String) -> Option<()> {
- let score = self.matcher.score(&name, &self.file_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Directory { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn label(
- &mut self,
- range: TextRange,
- name: &'a str,
- kind: Structure,
- header: Option<String>,
- footer: Option<&'a str>,
- text: String,
- ) -> Option<()> {
- let score = self.matcher.score(&text, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::Label {
- name,
- kind,
- header,
- footer,
- text,
- },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn tikz_library(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::TikzLibrary { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn user_command(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern[1..])?;
- self.items.push(Item {
- range,
- data: Data::UserCommand { name },
- preselect: false,
- score,
- });
-
- Some(())
- }
-
- pub fn user_environment(&mut self, range: TextRange, name: &'a str) -> Option<()> {
- let score = self.matcher.score(name, &self.text_pattern)?;
- self.items.push(Item {
- range,
- data: Data::UserEnvironment { name },
- preselect: Some(name) == self.preselect.as_deref(),
- score,
- });
-
- Some(())
- }
-
- pub fn finish(mut self) -> CompletionList {
- let mut list = CompletionList::default();
- list.items = std::mem::take(&mut self.items)
- .into_iter()
- .sorted_by(|a, b| {
- b.preselect
- .cmp(&a.preselect)
- .then_with(|| b.score.cmp(&a.score))
- .then_with(|| a.data.label().cmp(b.data.label()))
- })
- .dedup_by(|a, b| a.data.label() == b.data.label())
- .take(COMPLETION_LIMIT)
- .enumerate()
- .map(|(i, item)| self.convert_item(item, i))
- .collect();
-
- list.is_incomplete = self.always_incomplete || list.items.len() >= COMPLETION_LIMIT;
- list
- }
-
- fn convert_item(&self, item: Item, index: usize) -> CompletionItem {
- let range = self
- .context
- .document
- .line_index
- .line_col_lsp_range(item.range);
-
- let preselect = item.preselect;
- let mut item = match item.data {
- Data::EntryType { entry_type } => CompletionItem {
- label: entry_type.name.into(),
- kind: Some(Structure::Entry(entry_type.category).completion_kind()),
- documentation: entry_type.documentation.map(|value| {
- let kind = MarkupKind::Markdown;
- Documentation::MarkupContent(MarkupContent {
- kind,
- value: value.into(),
- })
- }),
- text_edit: Some(TextEdit::new(range, entry_type.name.into()).into()),
- ..CompletionItem::default()
- },
- Data::Field { field } => CompletionItem {
- label: field.name.into(),
- kind: Some(Structure::Field.completion_kind()),
- documentation: Some(Documentation::MarkupContent(MarkupContent {
- kind: MarkupKind::Markdown,
- value: field.documentation.into(),
- })),
- text_edit: Some(TextEdit::new(range, field.name.into()).into()),
- ..CompletionItem::default()
- },
- Data::Argument { name, image } => {
- let text_edit = TextEdit::new(range, String::from(name));
- CompletionItem {
- label: name.into(),
- kind: Some(Structure::Argument.completion_kind()),
- text_edit: Some(CompletionTextEdit::Edit(text_edit)),
- documentation: image.and_then(|base64| self.inline_image(name, base64)),
- ..CompletionItem::default()
- }
- }
- Data::BeginSnippet => {
- if self.snippets {
- CompletionItem {
- kind: Some(Structure::Snippet.completion_kind()),
- text_edit: Some(
- TextEdit::new(range, "begin{$1}\n\t\n\\end{$1}".into()).into(),
- ),
- insert_text_format: Some(InsertTextFormat::SNIPPET),
- ..CompletionItem::new_simple("begin".into(), self.component_detail(&[]))
- }
- } else {
- CompletionItem {
- kind: Some(Structure::Command.completion_kind()),
- text_edit: Some(TextEdit::new(range, "begin".to_string()).into()),
- ..CompletionItem::new_simple("begin".into(), self.component_detail(&[]))
- }
- }
- }
- Data::Citation {
- document,
- key,
- filter_text,
- category,
- } => CompletionItem {
- label: key.into(),
- kind: Some(Structure::Entry(category).completion_kind()),
- filter_text: Some(filter_text.into()),
- data: Some(
- serde_json::to_value(CompletionItemData::Citation {
- uri: document.uri.clone(),
- key: key.into(),
- })
- .unwrap(),
- ),
- text_edit: Some(TextEdit::new(range, key.into()).into()),
- ..CompletionItem::default()
- },
- Data::ComponentCommand {
- name,
- image,
- glyph,
- file_names,
- } => CompletionItem {
- label: name.into(),
- detail: Some(glyph.map_or_else(
- || self.component_detail(file_names),
- |glyph| format!("{}, {}", glyph, self.component_detail(file_names)),
- )),
- kind: Some(Structure::Command.completion_kind()),
- documentation: image.and_then(|base64| self.inline_image(name, base64)),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::ComponentEnvironment { name, file_names } => CompletionItem {
- kind: Some(Structure::Environment.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::new_simple(name.into(), self.component_detail(file_names))
- },
- Data::Class { name } => CompletionItem {
- label: name.into(),
- kind: Some(Structure::Class.completion_kind()),
- data: Some(serde_json::to_value(CompletionItemData::Package).unwrap()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::Package { name } => CompletionItem {
- label: name.into(),
- kind: Some(Structure::Package.completion_kind()),
- data: Some(serde_json::to_value(CompletionItemData::Class).unwrap()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::Color { name } => CompletionItem {
- label: name.into(),
- kind: Some(Structure::Color.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::ColorModel { name } => CompletionItem {
- label: name.into(),
- kind: Some(Structure::ColorModel.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::GlossaryEntry { name } => CompletionItem {
- label: name.clone(),
- kind: Some(Structure::GlossaryEntry.completion_kind()),
- text_edit: Some(TextEdit::new(range, name).into()),
- ..CompletionItem::default()
- },
- Data::File { name } => CompletionItem {
- label: name.clone(),
- kind: Some(Structure::File.completion_kind()),
- text_edit: Some(TextEdit::new(range, name).into()),
- ..CompletionItem::default()
- },
- Data::Directory { name } => CompletionItem {
- label: name.clone(),
- kind: Some(Structure::Folder.completion_kind()),
- text_edit: Some(TextEdit::new(range, name).into()),
- ..CompletionItem::default()
- },
- Data::Label {
- name,
- kind,
- header,
- footer,
- text,
- } => CompletionItem {
- label: name.into(),
- kind: Some(kind.completion_kind()),
- detail: header,
- documentation: footer.map(|footer| Documentation::String(footer.into())),
- filter_text: Some(text),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- Data::UserCommand { name } => {
- let detail = "user-defined".into();
- CompletionItem {
- kind: Some(Structure::Command.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::new_simple(name.into(), detail)
- }
- }
- Data::UserEnvironment { name } => {
- let detail = "user-defined".into();
- CompletionItem {
- kind: Some(Structure::Environment.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::new_simple(name.into(), detail)
- }
- }
- Data::TikzLibrary { name } => CompletionItem {
- label: name.into(),
- kind: Some(Structure::TikzLibrary.completion_kind()),
- text_edit: Some(TextEdit::new(range, name.into()).into()),
- ..CompletionItem::default()
- },
- };
-
- item.preselect = Some(preselect);
-
- if !self.item_kinds.contains(&item.kind.unwrap()) {
- item.kind = Some(CompletionItemKind::TEXT);
- }
-
- item.sort_text = Some(format!("{:0>2}", index));
- item
- }
-
- fn inline_image(&self, name: &str, base64: &str) -> Option<Documentation> {
- if self.markdown {
- let kind = MarkupKind::Markdown;
- let value = format!(
- "![{}](data:image/png;base64,{}|width=48,height=48)",
- name, base64
- );
-
- Some(Documentation::MarkupContent(MarkupContent { kind, value }))
- } else {
- None
- }
- }
-
- fn component_detail(&self, file_names: &[&str]) -> String {
- if file_names.is_empty() {
- "built-in".into()
- } else {
- file_names.join(", ")
- }
- }
-}
-
-#[derive(Debug, Clone)]
-struct Item<'a> {
- range: TextRange,
- data: Data<'a>,
- preselect: bool,
- score: i32,
-}
-
-#[derive(Debug, Clone)]
-enum Data<'a> {
- EntryType {
- entry_type: &'a BibtexEntryType<'a>,
- },
- Field {
- field: &'a BibtexFieldType<'a>,
- },
- Argument {
- name: &'a str,
- image: Option<&'a str>,
- },
- BeginSnippet,
- Citation {
- document: &'a Document,
- key: &'a str,
- filter_text: &'a str,
- category: BibtexEntryTypeCategory,
- },
- ComponentCommand {
- name: &'a str,
- image: Option<&'a str>,
- glyph: Option<&'a str>,
- file_names: &'a [&'a str],
- },
- ComponentEnvironment {
- name: &'a str,
- file_names: &'a [&'a str],
- },
- Class {
- name: &'a str,
- },
- Package {
- name: &'a str,
- },
- Color {
- name: &'a str,
- },
- ColorModel {
- name: &'a str,
- },
- GlossaryEntry {
- name: String,
- },
- File {
- name: String,
- },
- Directory {
- name: String,
- },
- Label {
- name: &'a str,
- kind: Structure,
- header: Option<String>,
- footer: Option<&'a str>,
- text: String,
- },
- UserCommand {
- name: &'a str,
- },
- UserEnvironment {
- name: &'a str,
- },
- TikzLibrary {
- name: &'a str,
- },
-}
-
-impl<'db> Data<'db> {
- pub fn label<'this: 'db>(&'this self) -> &'db str {
- match self {
- Self::EntryType { entry_type } => entry_type.name,
- Self::Field { field } => field.name,
- Self::Argument { name, .. } => name,
- Self::BeginSnippet => "begin",
- Self::Citation { key, .. } => key,
- Self::ComponentCommand { name, .. } => name,
- Self::ComponentEnvironment { name, .. } => name,
- Self::Class { name } => name,
- Self::Package { name } => name,
- Self::Color { name } => name,
- Self::ColorModel { name } => name,
- Self::GlossaryEntry { name } => name,
- Self::File { name } => name,
- Self::Directory { name } => name,
- Self::Label { name, .. } => name,
- Self::UserCommand { name } => name,
- Self::UserEnvironment { name } => name,
- Self::TikzLibrary { name } => name,
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub(crate) enum CompletionItemData {
- Package,
- Class,
- Citation { uri: Url, key: String },
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/citation.rs b/support/texlab/crates/texlab/src/features/completion/citation.rs
deleted file mode 100644
index b22f361f36..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/citation.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use rowan::{ast::AstNode, TextRange};
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let token = context.cursor.as_tex()?;
-
- let range = if token.kind() == latex::WORD {
- 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.project.documents {
- builder.citations(range, document);
- }
-
- Some(())
-}
-
-fn check_citation(context: &CursorContext) -> Option<()> {
- let (_, _, group) = context.find_curly_group_word_list()?;
- latex::Citation::cast(group.syntax().parent()?)?;
- Some(())
-}
-
-fn check_acronym(context: &CursorContext) -> Option<()> {
- let token = context.cursor.as_tex()?;
-
- let pair = token
- .parent_ancestors()
- .find_map(latex::KeyValuePair::cast)?;
- if pair.key()?.to_string() != "cite" {
- return None;
- }
-
- latex::AcronymDeclaration::cast(pair.syntax().parent()?.parent()?.parent()?)?;
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/color_model.rs b/support/texlab/crates/texlab/src/features/completion/color_model.rs
deleted file mode 100644
index 107cb95b19..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/color_model.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use rowan::{ast::AstNode, TextRange};
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-const MODEL_NAMES: &[&str] = &["gray", "rgb", "RGB", "HTML", "cmyk"];
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = check_color_definition(context).or_else(|| check_color_definition_set(context))?;
-
- for name in MODEL_NAMES {
- builder.color_model(range, name);
- }
-
- Some(())
-}
-
-fn check_color_definition(context: &CursorContext) -> Option<TextRange> {
- let (_, range, group) = context.find_curly_group_word()?;
-
- let definition = latex::ColorDefinition::cast(group.syntax().parent()?)?;
- definition
- .model()
- .filter(|model| model.syntax().text_range() == group.syntax().text_range())?;
- Some(range)
-}
-
-fn check_color_definition_set(context: &CursorContext) -> Option<TextRange> {
- let (_, range, group) = context.find_curly_group_word_list()?;
- let definition = latex::ColorSetDefinition::cast(group.syntax().parent()?)?;
- definition
- .model_list()
- .filter(|model| model.syntax().text_range() == group.syntax().text_range())?;
- Some(range)
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/component_command.rs b/support/texlab/crates/texlab/src/features/completion/component_command.rs
deleted file mode 100644
index 3810224a38..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/component_command.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context.cursor.command_range(context.offset)?;
-
- for package in context.included_packages() {
- for command in &package.commands {
- builder.component_command(
- range,
- &command.name,
- command.image,
- command.glyph.as_deref(),
- &package.file_names,
- );
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/component_environment.rs b/support/texlab/crates/texlab/src/features/completion/component_environment.rs
deleted file mode 100644
index 94921958cd..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/component_environment.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context.find_environment_name()?;
-
- for package in context.included_packages() {
- for name in &package.environments {
- builder.component_environment(range, name, &package.file_names);
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/entry_type.rs b/support/texlab/crates/texlab/src/features/completion/entry_type.rs
deleted file mode 100644
index 767839189d..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/entry_type.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use rowan::{TextRange, TextSize};
-use syntax::bibtex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context
- .cursor
- .as_bib()
- .filter(|token| token.kind() == bibtex::TYPE)
- .map(bibtex::SyntaxToken::text_range)
- .filter(|range| range.start() != context.offset)
- .map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))?;
-
- for entry_type in base_db::data::BIBTEX_ENTRY_TYPES {
- builder.entry_type(range, entry_type);
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/field.rs b/support/texlab/crates/texlab/src/features/completion/field.rs
deleted file mode 100644
index 01786b5e93..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/field.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use rowan::{ast::AstNode, TextRange};
-use syntax::bibtex::{self, HasName};
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let token = context.cursor.as_bib()?;
-
- 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.clone()) {
- if entry.name_token()?.text_range() == token.text_range() {
- return None;
- }
- } else {
- bibtex::Field::cast(parent)?;
- }
-
- for field in base_db::data::BIBTEX_FIELD_TYPES {
- builder.field(range, field);
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/glossary_ref.rs b/support/texlab/crates/texlab/src/features/completion/glossary_ref.rs
deleted file mode 100644
index 0d218a1b03..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/glossary_ref.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use base_db::DocumentData;
-use rowan::ast::AstNode;
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let (_, range, group) = context.find_curly_group_word()?;
- latex::GlossaryEntryReference::cast(group.syntax().parent()?)?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
-
- for node in data.root_node().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())
- {
- builder.glossary_entry(range, name);
- } else if let Some(name) = latex::AcronymDefinition::cast(node)
- .and_then(|entry| entry.name())
- .and_then(|name| name.key())
- .map(|name| name.to_string())
- {
- builder.glossary_entry(range, name);
- }
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/import.rs b/support/texlab/crates/texlab/src/features/completion/import.rs
deleted file mode 100644
index cb8c20a814..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/import.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use rowan::ast::AstNode;
-use rustc_hash::FxHashSet;
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let (_, range, group) = context.find_curly_group_word_list()?;
-
- let kind = group.syntax().parent()?.kind();
- let extension = match kind {
- latex::PACKAGE_INCLUDE => "sty",
- latex::CLASS_INCLUDE => "cls",
- _ => return Some(()),
- };
-
- let mut file_names = FxHashSet::default();
- for file_name in completion_data::DATABASE
- .iter()
- .flat_map(|package| package.file_names.iter())
- .filter(|file_name| file_name.ends_with(extension))
- {
- file_names.insert(file_name);
- let stem = &file_name[0..file_name.len() - 4];
- if kind == latex::PACKAGE_INCLUDE {
- builder.package(range, stem);
- } else {
- builder.class(range, stem);
- }
- }
-
- let file_name_db = &context.workspace.distro().file_name_db;
- for file_name in file_name_db
- .iter()
- .map(|(file_name, _)| file_name)
- .filter(|file_name| file_name.ends_with(extension) && !file_names.contains(file_name))
- {
- let stem = &file_name[0..file_name.len() - 4];
- if kind == latex::PACKAGE_INCLUDE {
- builder.package(range, stem);
- } else {
- builder.class(range, stem);
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/label.rs b/support/texlab/crates/texlab/src/features/completion/label.rs
deleted file mode 100644
index be6e1b9ab7..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/label.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use base_db::{
- semantics::tex::LabelKind,
- util::{render_label, RenderedObject},
- DocumentData,
-};
-use rowan::{ast::AstNode, TextRange};
-use syntax::latex;
-
-use crate::util::{cursor::CursorContext, lsp_enums::Structure};
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let (range, is_math) = find_reference(context).or_else(|| find_reference_range(context))?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
- for label in data
- .semantics
- .labels
- .iter()
- .filter(|label| label.kind == LabelKind::Definition)
- {
- match render_label(context.workspace, &context.project, label) {
- Some(rendered_label) => {
- let kind = match &rendered_label.object {
- RenderedObject::Section { .. } => Structure::Section,
- RenderedObject::Float { .. } => Structure::Float,
- RenderedObject::Theorem { .. } => Structure::Theorem,
- RenderedObject::Equation => Structure::Equation,
- RenderedObject::EnumItem => Structure::Item,
- };
-
- if is_math && kind != Structure::Equation {
- continue;
- }
-
- let header = rendered_label.detail();
- let footer = match &rendered_label.object {
- RenderedObject::Float { caption, .. } => Some(*caption),
- _ => None,
- };
-
- let text = format!("{} {}", label.name.text, rendered_label.reference());
-
- builder.label(range, &label.name.text, kind, header, footer, text);
- }
- None => {
- let kind = Structure::Label;
- let header = None;
- let footer = None;
- let text = label.name.text.clone();
- builder.label(range, &label.name.text, kind, header, footer, text);
- }
- }
- }
- }
-
- Some(())
-}
-
-fn find_reference(context: &CursorContext) -> Option<(TextRange, bool)> {
- let (_, range, group) = context.find_curly_group_word_list()?;
- let reference = latex::LabelReference::cast(group.syntax().parent()?)?;
- let is_math = reference.command()?.text() == "\\eqref";
- Some((range, is_math))
-}
-
-fn find_reference_range(context: &CursorContext) -> Option<(TextRange, bool)> {
- let (_, range, group) = context.find_curly_group_word()?;
- latex::LabelReferenceRange::cast(group.syntax().parent()?)?;
- Some((range, false))
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/theorem.rs b/support/texlab/crates/texlab/src/features/completion/theorem.rs
deleted file mode 100644
index a0ff71c9f4..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/theorem.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use base_db::DocumentData;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context.find_environment_name()?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
- for theorem in &data.semantics.theorem_definitions {
- builder.user_environment(range, &theorem.name.text);
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/user_command.rs b/support/texlab/crates/texlab/src/features/completion/user_command.rs
deleted file mode 100644
index e1c517e6aa..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/user_command.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use base_db::DocumentData;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context.cursor.command_range(context.offset)?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
- for name in data
- .semantics
- .commands
- .iter()
- .filter(|name| name.range != range)
- {
- builder.user_command(range, &name.text);
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/completion/user_environment.rs b/support/texlab/crates/texlab/src/features/completion/user_environment.rs
deleted file mode 100644
index 109925d623..0000000000
--- a/support/texlab/crates/texlab/src/features/completion/user_environment.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use base_db::DocumentData;
-
-use crate::util::cursor::CursorContext;
-
-use super::builder::CompletionBuilder;
-
-pub fn complete<'db>(
- context: &'db CursorContext,
- builder: &mut CompletionBuilder<'db>,
-) -> Option<()> {
- let range = context.find_environment_name()?;
-
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
- for name in data
- .semantics
- .environments
- .iter()
- .filter(|name| name.range != range)
- {
- builder.user_environment(range, &name.text);
- }
- }
-
- Some(())
-}
diff --git a/support/texlab/crates/texlab/src/features/highlight.rs b/support/texlab/crates/texlab/src/features/highlight.rs
index c2f5337aec..63fc8ef9e2 100644
--- a/support/texlab/crates/texlab/src/features/highlight.rs
+++ b/support/texlab/crates/texlab/src/features/highlight.rs
@@ -1,15 +1,17 @@
mod label;
use base_db::Workspace;
-use lsp_types::{DocumentHighlight, Position, Url};
+use lsp_types::DocumentHighlight;
-use crate::util::cursor::CursorContext;
+use crate::util::line_index_ext::LineIndexExt;
pub fn find_all(
workspace: &Workspace,
- uri: &Url,
- position: Position,
+ params: &lsp_types::DocumentHighlightParams,
) -> Option<Vec<DocumentHighlight>> {
- let context = CursorContext::new(workspace, uri, position, ())?;
- label::find_highlights(&context)
+ let uri = &params.text_document_position_params.text_document.uri;
+ let document = workspace.lookup(uri)?;
+ let position = params.text_document_position_params.position;
+ let offset = document.line_index.offset_lsp(position);
+ label::find_highlights(document, offset)
}
diff --git a/support/texlab/crates/texlab/src/features/highlight/label.rs b/support/texlab/crates/texlab/src/features/highlight/label.rs
index 171b5e4301..dc0a60ab65 100644
--- a/support/texlab/crates/texlab/src/features/highlight/label.rs
+++ b/support/texlab/crates/texlab/src/features/highlight/label.rs
@@ -1,26 +1,33 @@
-use base_db::{semantics::tex::LabelKind, DocumentData};
-use lsp_types::{DocumentHighlight, DocumentHighlightKind};
+use base_db::{semantics::tex::LabelKind, Document};
+use lsp_types::DocumentHighlight;
+use rowan::TextSize;
-use crate::util::{cursor::CursorContext, line_index_ext::LineIndexExt};
+use crate::util::line_index_ext::LineIndexExt;
-pub fn find_highlights(context: &CursorContext) -> Option<Vec<DocumentHighlight>> {
- let (name_text, _) = context.find_label_name_key()?;
-
- let DocumentData::Tex(data) = &context.document.data else { return None };
+pub fn find_highlights(
+ document: &Document,
+ offset: TextSize,
+) -> Option<Vec<lsp_types::DocumentHighlight>> {
+ let data = document.data.as_tex()?;
+ let cursor = data
+ .semantics
+ .labels
+ .iter()
+ .find(|label| label.name.range.contains(offset))?;
let mut highlights = Vec::new();
- let line_index = &context.document.line_index;
+ let line_index = &document.line_index;
for label in data
.semantics
.labels
.iter()
- .filter(|label| label.name.text == name_text)
+ .filter(|label| label.name.text == cursor.name.text)
{
let range = line_index.line_col_lsp_range(label.name.range);
let kind = Some(match label.kind {
- LabelKind::Definition => DocumentHighlightKind::WRITE,
- LabelKind::Reference => DocumentHighlightKind::READ,
- LabelKind::ReferenceRange => DocumentHighlightKind::READ,
+ LabelKind::Definition => lsp_types::DocumentHighlightKind::WRITE,
+ LabelKind::Reference => lsp_types::DocumentHighlightKind::READ,
+ LabelKind::ReferenceRange => lsp_types::DocumentHighlightKind::READ,
});
highlights.push(DocumentHighlight { range, kind });
diff --git a/support/texlab/crates/texlab/src/features/rename.rs b/support/texlab/crates/texlab/src/features/rename.rs
index 3746b9ca44..1f48a449c0 100644
--- a/support/texlab/crates/texlab/src/features/rename.rs
+++ b/support/texlab/crates/texlab/src/features/rename.rs
@@ -1,64 +1,47 @@
-mod command;
-mod entry;
-mod label;
+use std::collections::HashMap;
-use base_db::{Document, Workspace};
-use lsp_types::{Position, Range, TextEdit, Url, WorkspaceEdit};
-use rowan::TextRange;
-use rustc_hash::FxHashMap;
+use base_db::{FeatureParams, Workspace};
+use rename::RenameParams;
-use crate::util::{cursor::CursorContext, line_index_ext::LineIndexExt};
+use crate::util::line_index_ext::LineIndexExt;
-pub fn prepare_rename_all(workspace: &Workspace, uri: &Url, position: Position) -> Option<Range> {
- let context = CursorContext::new(workspace, uri, position, ())?;
- let range = entry::prepare_rename(&context)
- .or_else(|| label::prepare_rename(&context))
- .or_else(|| command::prepare_rename(&context))?;
-
- Some(context.document.line_index.line_col_lsp_range(range))
+pub fn prepare_rename_all(
+ workspace: &Workspace,
+ params: &lsp_types::TextDocumentPositionParams,
+) -> Option<lsp_types::Range> {
+ let params = create_params(workspace, params)?;
+ let range = rename::prepare_rename(&params)?;
+ Some(params.inner.document.line_index.line_col_lsp_range(range))
}
pub fn rename_all(
workspace: &Workspace,
- uri: &Url,
- position: Position,
- new_name: String,
-) -> Option<WorkspaceEdit> {
- let context = CursorContext::new(workspace, uri, position, Params { new_name })?;
- let result = entry::rename(&context)
- .or_else(|| label::rename(&context))
- .or_else(|| command::rename(&context))?;
-
- let changes = result
- .changes
- .into_iter()
- .map(|(document, old_edits)| {
- let new_edits = old_edits
- .into_iter()
- .map(|Indel { delete, insert }| {
- TextEdit::new(document.line_index.line_col_lsp_range(delete), insert)
- })
- .collect();
-
- (document.uri.clone(), new_edits)
- })
- .collect();
-
- Some(WorkspaceEdit::new(changes))
-}
-
-#[derive(Debug)]
-struct Params {
- new_name: String,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-struct Indel {
- delete: TextRange,
- insert: String,
+ params: &lsp_types::RenameParams,
+) -> Option<lsp_types::WorkspaceEdit> {
+ let new_name = &params.new_name;
+ let params = create_params(workspace, &params.text_document_position)?;
+ let result = rename::rename(&params);
+
+ let mut changes = HashMap::default();
+ for (document, ranges) in result.changes {
+ let mut edits = Vec::new();
+ for range in ranges {
+ let range = document.line_index.line_col_lsp_range(range);
+ edits.push(lsp_types::TextEdit::new(range, new_name.clone()));
+ }
+
+ changes.insert(document.uri.clone(), edits);
+ }
+
+ Some(lsp_types::WorkspaceEdit::new(changes))
}
-#[derive(Debug)]
-struct RenameResult<'a> {
- changes: FxHashMap<&'a Document, Vec<Indel>>,
+fn create_params<'db>(
+ workspace: &'db Workspace,
+ params: &lsp_types::TextDocumentPositionParams,
+) -> Option<RenameParams<'db>> {
+ let document = workspace.lookup(&params.text_document.uri)?;
+ let inner = FeatureParams::new(workspace, document);
+ let offset = document.line_index.offset_lsp(params.position);
+ Some(RenameParams { inner, offset })
}
diff --git a/support/texlab/crates/texlab/src/features/rename/command.rs b/support/texlab/crates/texlab/src/features/rename/command.rs
deleted file mode 100644
index 5cb14f80d1..0000000000
--- a/support/texlab/crates/texlab/src/features/rename/command.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use base_db::DocumentData;
-use rowan::{TextRange, TextSize};
-use rustc_hash::FxHashMap;
-use syntax::latex;
-
-use crate::util::cursor::CursorContext;
-
-use super::{Indel, Params, RenameResult};
-
-pub(super) fn prepare_rename<T>(context: &CursorContext<T>) -> Option<TextRange> {
- context.cursor.command_range(context.offset)
-}
-
-pub(super) fn rename<'a>(context: &CursorContext<'a, Params>) -> Option<RenameResult<'a>> {
- prepare_rename(context)?;
- let name = context.cursor.as_tex()?.text();
- let mut changes = FxHashMap::default();
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
-
- let root = data.root_node();
- let edits = root
- .descendants_with_tokens()
- .filter_map(|element| element.into_token())
- .filter(|token| token.kind() == latex::COMMAND_NAME && token.text() == name)
- .map(|token| {
- let range = token.text_range();
- Indel {
- delete: TextRange::new(range.start() + TextSize::from(1), range.end()),
- insert: context.params.new_name.clone(),
- }
- })
- .collect();
-
- changes.insert(*document, edits);
- }
-
- Some(RenameResult { changes })
-}
diff --git a/support/texlab/crates/texlab/src/features/rename/entry.rs b/support/texlab/crates/texlab/src/features/rename/entry.rs
deleted file mode 100644
index 6a6037c999..0000000000
--- a/support/texlab/crates/texlab/src/features/rename/entry.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use base_db::{Document, DocumentData};
-use rowan::TextRange;
-use rustc_hash::FxHashMap;
-
-use crate::util::cursor::CursorContext;
-
-use super::{Indel, Params, RenameResult};
-
-pub(super) fn prepare_rename<T>(context: &CursorContext<T>) -> Option<TextRange> {
- let (_, range) = context
- .find_citation_key_word()
- .or_else(|| context.find_entry_key())?;
-
- Some(range)
-}
-
-pub(super) fn rename<'a>(context: &CursorContext<'a, Params>) -> Option<RenameResult<'a>> {
- prepare_rename(context)?;
- let (key_text, _) = context
- .find_citation_key_word()
- .or_else(|| context.find_entry_key())?;
-
- let mut changes: FxHashMap<&Document, Vec<Indel>> = FxHashMap::default();
- for document in &context.project.documents {
- if let DocumentData::Tex(data) = &document.data {
- let edits = data
- .semantics
- .citations
- .iter()
- .filter(|citation| citation.name.text == key_text)
- .map(|citation| Indel {
- delete: citation.name.range,
- insert: context.params.new_name.clone(),
- })
- .collect();
-
- changes.insert(document, edits);
- } else if let DocumentData::Bib(data) = &document.data {
- let edits = data
- .semantics
- .entries
- .iter()
- .filter(|entry| entry.name.text == key_text)
- .map(|entry| Indel {
- delete: entry.name.range,
- insert: context.params.new_name.clone(),
- })
- .collect();
-
- changes.insert(document, edits);
- }
- }
-
- Some(RenameResult { changes })
-}
diff --git a/support/texlab/crates/texlab/src/features/rename/label.rs b/support/texlab/crates/texlab/src/features/rename/label.rs
deleted file mode 100644
index 803fb61989..0000000000
--- a/support/texlab/crates/texlab/src/features/rename/label.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use base_db::DocumentData;
-use rowan::TextRange;
-use rustc_hash::FxHashMap;
-
-use crate::util::cursor::CursorContext;
-
-use super::{Indel, Params, RenameResult};
-
-pub(super) fn prepare_rename<T>(context: &CursorContext<T>) -> Option<TextRange> {
- let (_, range) = context.find_label_name_key()?;
- Some(range)
-}
-
-pub(super) fn rename<'a>(context: &CursorContext<'a, Params>) -> Option<RenameResult<'a>> {
- prepare_rename(context)?;
- let (name_text, _) = context.find_label_name_key()?;
-
- let mut changes = FxHashMap::default();
- for document in &context.project.documents {
- let DocumentData::Tex(data) = &document.data else { continue };
-
- let edits = data
- .semantics
- .labels
- .iter()
- .filter(|label| label.name.text == name_text)
- .map(|label| Indel {
- delete: label.name.range,
- insert: context.params.new_name.clone(),
- })
- .collect();
-
- changes.insert(*document, edits);
- }
-
- Some(RenameResult { changes })
-}
diff --git a/support/texlab/crates/texlab/src/server.rs b/support/texlab/crates/texlab/src/server.rs
index 8f58bb3826..5a0c48960e 100644
--- a/support/texlab/crates/texlab/src/server.rs
+++ b/support/texlab/crates/texlab/src/server.rs
@@ -29,7 +29,7 @@ use threadpool::ThreadPool;
use crate::{
client::LspClient,
features::{
- completion::{self, builder::CompletionItemData},
+ completion::{self, ResolveInfo},
definition, folding, formatting, highlight, hover, inlay_hint, link, reference, rename,
symbols,
},
@@ -528,24 +528,16 @@ impl Server {
Ok(())
}
- fn completion(&self, id: RequestId, params: CompletionParams) -> Result<()> {
- let mut uri = params.text_document_position.text_document.uri;
- normalize_uri(&mut uri);
+ fn completion(&self, id: RequestId, mut params: CompletionParams) -> Result<()> {
+ normalize_uri(&mut params.text_document_position.text_document.uri);
let position = params.text_document_position.position;
let client_capabilities = Arc::clone(&self.client_capabilities);
let client_info = self.client_info.clone();
-
- self.update_cursor(&uri, position);
-
+ self.update_cursor(&params.text_document_position.text_document.uri, position);
self.run_query(id, move |db| {
- completion::complete(
- db,
- &uri,
- position,
- &client_capabilities,
- client_info.as_deref(),
- )
+ completion::complete(db, &params, &client_capabilities, client_info.as_deref())
});
+
Ok(())
}
@@ -566,7 +558,7 @@ impl Server {
.clone()
.map(|data| serde_json::from_value(data).unwrap())
{
- Some(CompletionItemData::Package | CompletionItemData::Class) => {
+ Some(ResolveInfo::Package | ResolveInfo::DocumentClass) => {
item.documentation = completion_data::DATABASE
.meta(&item.label)
.and_then(|meta| meta.description.as_deref())
@@ -577,7 +569,7 @@ impl Server {
})
});
}
- Some(CompletionItemData::Citation { uri, key }) => {
+ Some(ResolveInfo::Citation { uri, key }) => {
if let Some(data) = workspace
.lookup(&uri)
.and_then(|document| document.data.as_bib())
@@ -645,34 +637,24 @@ impl Server {
Ok(())
}
- fn prepare_rename(&self, id: RequestId, params: TextDocumentPositionParams) -> Result<()> {
- let mut uri = params.text_document.uri;
- normalize_uri(&mut uri);
- self.run_query(id, move |db| {
- rename::prepare_rename_all(db, &uri, params.position)
- });
-
+ fn prepare_rename(&self, id: RequestId, mut params: TextDocumentPositionParams) -> Result<()> {
+ normalize_uri(&mut params.text_document.uri);
+ self.run_query(id, move |db| rename::prepare_rename_all(db, &params));
Ok(())
}
- fn rename(&self, id: RequestId, params: RenameParams) -> Result<()> {
- let mut uri = params.text_document_position.text_document.uri;
- normalize_uri(&mut uri);
- let position = params.text_document_position.position;
- self.run_query(id, move |db| {
- rename::rename_all(db, &uri, position, params.new_name)
- });
-
+ fn rename(&self, id: RequestId, mut params: RenameParams) -> Result<()> {
+ normalize_uri(&mut params.text_document_position.text_document.uri);
+ self.run_query(id, move |db| rename::rename_all(db, &params));
Ok(())
}
- fn document_highlight(&self, id: RequestId, params: DocumentHighlightParams) -> Result<()> {
- let mut uri = params.text_document_position_params.text_document.uri;
- normalize_uri(&mut uri);
- let position = params.text_document_position_params.position;
+ fn document_highlight(&self, id: RequestId, mut params: DocumentHighlightParams) -> Result<()> {
+ normalize_uri(&mut params.text_document_position_params.text_document.uri);
self.run_query(id, move |db| {
- highlight::find_all(db, &uri, position).unwrap_or_default()
+ highlight::find_all(db, &params).unwrap_or_default()
});
+
Ok(())
}
diff --git a/support/texlab/crates/texlab/src/server/options.rs b/support/texlab/crates/texlab/src/server/options.rs
index 30eff447d7..3a2bf1bb10 100644
--- a/support/texlab/crates/texlab/src/server/options.rs
+++ b/support/texlab/crates/texlab/src/server/options.rs
@@ -81,6 +81,7 @@ pub struct BuildOptions {
pub struct ChktexOptions {
pub on_open_and_save: bool,
pub on_edit: bool,
+ pub additional_args: Option<Vec<String>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
@@ -199,6 +200,8 @@ impl From<Options> for Config {
config.diagnostics.chktex.on_open = value.chktex.on_open_and_save;
config.diagnostics.chktex.on_save = value.chktex.on_open_and_save;
config.diagnostics.chktex.on_edit = value.chktex.on_edit;
+ config.diagnostics.chktex.additional_args =
+ value.chktex.additional_args.unwrap_or_default();
config.formatting.tex_formatter = match value.latex_formatter {
LatexFormatter::None => Formatter::Null,
diff --git a/support/texlab/crates/texlab/src/util.rs b/support/texlab/crates/texlab/src/util.rs
index 5a4257efaa..ecc7b850d7 100644
--- a/support/texlab/crates/texlab/src/util.rs
+++ b/support/texlab/crates/texlab/src/util.rs
@@ -1,18 +1,10 @@
pub mod capabilities;
pub mod chktex;
-pub mod cursor;
pub mod diagnostics;
pub mod line_index_ext;
pub mod lsp_enums;
-use std::path::PathBuf;
-
-use lsp_types::Url;
-use once_cell::sync::Lazy;
-
-pub static HOME_DIR: Lazy<Option<PathBuf>> = Lazy::new(dirs::home_dir);
-
-pub fn normalize_uri(uri: &mut Url) {
+pub fn normalize_uri(uri: &mut lsp_types::Url) {
if let Some(mut segments) = uri.path_segments() {
if let Some(mut path) = segments.next().and_then(fix_drive_letter) {
for segment in segments {
diff --git a/support/texlab/crates/texlab/src/util/chktex.rs b/support/texlab/crates/texlab/src/util/chktex.rs
index 91d74a9cdc..6166108707 100644
--- a/support/texlab/crates/texlab/src/util/chktex.rs
+++ b/support/texlab/crates/texlab/src/util/chktex.rs
@@ -16,6 +16,7 @@ use regex::Regex;
pub struct Command {
text: String,
working_dir: PathBuf,
+ additional_args: Vec<String>,
}
impl Command {
@@ -39,13 +40,21 @@ impl Command {
log::debug!("Calling ChkTeX from directory: {}", working_dir.display());
let text = document.text.clone();
-
- Some(Self { text, working_dir })
+ let config = &workspace.config().diagnostics.chktex;
+ let additional_args = config.additional_args.clone();
+ Some(Self {
+ text,
+ working_dir,
+ additional_args,
+ })
}
- pub fn run(self) -> std::io::Result<Vec<Diagnostic>> {
+ pub fn run(mut self) -> std::io::Result<Vec<Diagnostic>> {
+ let mut args = vec!["-I0".into(), "-f%l:%c:%d:%k:%n:%m\n".into()];
+ args.append(&mut self.additional_args);
+
let mut child = std::process::Command::new("chktex")
- .args(["-I0", "-f%l:%c:%d:%k:%n:%m\n"])
+ .args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
diff --git a/support/texlab/crates/texlab/src/util/cursor.rs b/support/texlab/crates/texlab/src/util/cursor.rs
deleted file mode 100644
index 16b00776d4..0000000000
--- a/support/texlab/crates/texlab/src/util/cursor.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-use base_db::{Document, DocumentData, Project, Workspace};
-use lsp_types::{Position, Url};
-use rowan::{ast::AstNode, TextRange, TextSize};
-use syntax::{bibtex, latex};
-
-use super::line_index_ext::LineIndexExt;
-
-#[derive(Debug)]
-pub enum Cursor {
- Tex(latex::SyntaxToken),
- Bib(bibtex::SyntaxToken),
- Nothing,
-}
-
-impl Cursor {
- pub fn new_tex(
- left: Option<latex::SyntaxToken>,
- right: Option<latex::SyntaxToken>,
- ) -> Option<Self> {
- let left = left?;
- let right = right?;
-
- if left.kind() == latex::COMMAND_NAME {
- return Some(Self::Tex(left));
- }
-
- if right.kind() == latex::WORD {
- return Some(Self::Tex(right));
- }
-
- if left.kind() == latex::WORD {
- return Some(Self::Tex(left));
- }
-
- if right.kind() == latex::COMMAND_NAME {
- return Some(Self::Tex(right));
- }
-
- if left.kind() == latex::WHITESPACE && left.parent()?.kind() == latex::KEY {
- return Some(Self::Tex(left));
- }
-
- if matches!(right.kind(), latex::WHITESPACE | latex::LINE_BREAK)
- && right.parent()?.kind() == latex::KEY
- {
- return Some(Self::Tex(right));
- }
-
- Some(Self::Tex(right))
- }
-
- pub fn new_bib(
- left: Option<bibtex::SyntaxToken>,
- right: Option<bibtex::SyntaxToken>,
- ) -> Option<Self> {
- let left = left?;
- let right = right?;
-
- if right.kind() == bibtex::TYPE {
- return Some(Self::Bib(right));
- }
-
- if left.kind() == bibtex::TYPE {
- return Some(Self::Bib(left));
- }
-
- if matches!(left.kind(), bibtex::COMMAND_NAME | bibtex::ACCENT_NAME) {
- return Some(Self::Bib(left));
- }
-
- if matches!(right.kind(), bibtex::WORD | bibtex::NAME) {
- return Some(Self::Bib(right));
- }
-
- if matches!(left.kind(), bibtex::WORD | bibtex::NAME) {
- return Some(Self::Bib(left));
- }
-
- if matches!(right.kind(), bibtex::COMMAND_NAME | bibtex::ACCENT_NAME) {
- return Some(Self::Bib(right));
- }
-
- Some(Self::Bib(right))
- }
-
- pub fn as_tex(&self) -> Option<&latex::SyntaxToken> {
- if let Self::Tex(v) = self {
- Some(v)
- } else {
- None
- }
- }
-
- pub fn as_bib(&self) -> Option<&bibtex::SyntaxToken> {
- if let Self::Bib(v) = self {
- Some(v)
- } else {
- None
- }
- }
-
- pub fn command_range(&self, offset: TextSize) -> Option<TextRange> {
- self.as_tex()
- .filter(|token| token.kind() == latex::COMMAND_NAME)
- .filter(|token| token.text_range().start() != offset)
- .map(|token| token.text_range())
- .map(|range| TextRange::new(range.start() + TextSize::from(1), range.end()))
- .or_else(|| {
- self.as_bib()
- .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()))
- })
- }
-}
-
-pub struct CursorContext<'a, T = ()> {
- pub workspace: &'a Workspace,
- pub document: &'a Document,
- pub project: Project<'a>,
- pub cursor: Cursor,
- pub offset: TextSize,
- pub params: T,
-}
-
-impl<'a, T> CursorContext<'a, T> {
- pub fn new(workspace: &'a Workspace, uri: &Url, position: Position, params: T) -> Option<Self> {
- let document = workspace.lookup(uri)?;
- let offset = document.line_index.offset_lsp(position);
-
- let cursor = match &document.data {
- DocumentData::Tex(data) => {
- let root = data.root_node();
- let left = root.token_at_offset(offset).left_biased();
- let right = root.token_at_offset(offset).right_biased();
- Cursor::new_tex(left, right)
- }
- DocumentData::Bib(data) => {
- let root = data.root_node();
- let left = root.token_at_offset(offset).left_biased();
- let right = root.token_at_offset(offset).right_biased();
- Cursor::new_bib(left, right)
- }
- DocumentData::Aux(_)
- | DocumentData::Log(_)
- | DocumentData::Root
- | DocumentData::Tectonic => None,
- };
-
- Some(Self {
- workspace,
- document,
- project: workspace.project(document),
- cursor: cursor.unwrap_or(Cursor::Nothing),
- offset,
- params,
- })
- }
-
- 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)> {
- let word = self
- .cursor
- .as_tex()
- .filter(|token| token.kind() == latex::WORD)?;
-
- 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(), latex::small_range(&key)))
- }
-
- pub fn find_citation_key_command(&self) -> Option<(String, TextRange)> {
- let command = self.cursor.as_tex()?;
-
- let citation = latex::Citation::cast(command.parent()?)?;
- let key = citation.key_list()?.keys().next()?;
- Some((key.to_string(), latex::small_range(&key)))
- }
-
- pub fn find_entry_key(&self) -> Option<(String, TextRange)> {
- let key = self
- .cursor
- .as_bib()
- .filter(|token| token.kind() == bibtex::NAME)?;
-
- bibtex::Entry::cast(key.parent()?)?;
- Some((key.to_string(), key.text_range()))
- }
-
- pub fn find_label_name_key(&self) -> Option<(String, TextRange)> {
- let name = self
- .cursor
- .as_tex()
- .filter(|token| token.kind() == latex::WORD)?;
-
- 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(), latex::small_range(&key)))
- } else {
- None
- }
- }
-
- pub fn find_label_name_command(&self) -> Option<(String, TextRange)> {
- let node = self.cursor.as_tex()?.parent()?;
- if let Some(label) = latex::LabelDefinition::cast(node.clone()) {
- let name = label.name()?.key()?;
- 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(), latex::small_range(&name)))
- } else if let Some(label) = latex::LabelReferenceRange::cast(node) {
- let name = label.from()?.key()?;
- Some((name.to_string(), latex::small_range(&name)))
- } else {
- None
- }
- }
-
- pub fn find_environment_name(&self) -> Option<TextRange> {
- let (_, range, group) = self.find_curly_group_word()?;
-
- if !matches!(group.syntax().parent()?.kind(), latex::BEGIN | latex::END) {
- return None;
- }
-
- Some(range)
- }
-
- pub fn find_environment(&self) -> Option<(latex::Key, latex::Key)> {
- let token = self.cursor.as_tex()?;
- let env = token
- .parent_ancestors()
- .find_map(latex::Environment::cast)?;
-
- let beg = env.begin()?.name()?.key()?;
- let end = env.end()?.name()?.key()?;
- Some((beg, end))
- }
-
- pub fn find_curly_group_word(&self) -> Option<(String, TextRange, latex::CurlyGroupWord)> {
- let token = self.cursor.as_tex()?;
- let key = latex::Key::cast(token.parent()?);
-
- let group = key
- .as_ref()
- .and_then(|key| key.syntax().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(), latex::small_range(&key), group.clone()))
- .or_else(|| Some((String::new(), TextRange::empty(self.offset), group)))
- }
-
- pub fn find_curly_group_word_list(
- &self,
- ) -> Option<(String, TextRange, latex::CurlyGroupWordList)> {
- let token = self.cursor.as_tex()?;
- let key = latex::Key::cast(token.parent()?);
-
- let group = key
- .as_ref()
- .and_then(|key| key.syntax().parent())
- .unwrap_or(token.parent()?);
-
- let group = latex::CurlyGroupWordList::cast(group)
- .filter(|group| self.is_inside_latex_curly(group))?;
-
- key.map(|key| {
- let range = if group
- .syntax()
- .last_token()
- .map_or(false, |tok| tok.kind() != latex::R_CURLY)
- {
- TextRange::new(latex::small_range(&key).start(), token.text_range().end())
- } else {
- latex::small_range(&key)
- };
-
- (key.to_string(), range, group.clone())
- })
- .or_else(|| Some((String::new(), TextRange::empty(self.offset), group)))
- }
-
- pub fn included_packages(&self) -> impl Iterator<Item = &completion_data::Package<'_>> + '_ {
- let db = &completion_data::DATABASE;
- self.project
- .documents
- .iter()
- .filter_map(|document| document.data.as_tex())
- .flat_map(|data| data.semantics.links.iter())
- .filter_map(|link| link.package_name())
- .filter_map(|name| db.find(&name))
- .chain(std::iter::once(db.kernel()))
- .flat_map(|pkg| {
- pkg.references
- .iter()
- .filter_map(|name| db.find(name))
- .chain(std::iter::once(pkg))
- })
- }
-}
diff --git a/support/texlab/crates/texlab/tests/lsp/text_document.rs b/support/texlab/crates/texlab/tests/lsp/text_document.rs
index 729ab4283c..8276c43815 100644
--- a/support/texlab/crates/texlab/tests/lsp/text_document.rs
+++ b/support/texlab/crates/texlab/tests/lsp/text_document.rs
@@ -1,7 +1,5 @@
-mod completion;
mod document_highlight;
mod document_link;
mod document_symbol;
mod formatting;
mod inlay_hint;
-mod rename;
diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs b/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs
deleted file mode 100644
index e76f4360bd..0000000000
--- a/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs
+++ /dev/null
@@ -1,914 +0,0 @@
-use insta::assert_json_snapshot;
-use itertools::Itertools;
-use lsp_types::{
- request::{Completion, ResolveCompletionItem},
- CompletionItem, CompletionParams, CompletionResponse, CompletionTextEdit, Range,
-};
-
-use crate::fixture::TestBed;
-
-fn complete(fixture: &str) -> Vec<CompletionItem> {
- let test_bed = TestBed::new(fixture).unwrap();
- test_bed
- .initialize(
- serde_json::from_value(serde_json::json!({
- "textDocument": {
- "completion": {
- "completionItem": {
- "documentationFormat": ["plaintext", "markdown"]
- }
- }
- }
- }))
- .unwrap(),
- )
- .unwrap();
-
- let text_document_position = test_bed.cursor().unwrap();
- let position = text_document_position.position;
- let range = test_bed
- .locations()
- .first()
- .map_or_else(|| Range::new(position, position), |location| location.range);
-
- let items = match test_bed
- .client()
- .send_request::<Completion>(CompletionParams {
- text_document_position,
- partial_result_params: Default::default(),
- work_done_progress_params: Default::default(),
- context: None,
- })
- .unwrap()
- {
- Some(CompletionResponse::Array(items)) => items,
- Some(CompletionResponse::List(list)) => list.items,
- None => Vec::new(),
- };
-
- for item in &items {
- if let Some(CompletionTextEdit::Edit(edit)) = item.text_edit.as_ref() {
- assert_eq!(edit.range, range);
- }
- }
-
- items
- .into_iter()
- .take(5)
- .map(|item| {
- let mut item = test_bed
- .client()
- .send_request::<ResolveCompletionItem>(item)
- .unwrap();
-
- item.data = None;
- item.kind = None;
- item.sort_text = None;
- item.documentation = None;
- item.text_edit = None;
- item
- })
- .sorted_by(|item1, item2| item1.label.cmp(&item2.label))
- .collect()
-}
-
-#[test]
-fn acronym_ref_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\acrshort{f}
- |
- ^"#
- ));
-}
-
-#[test]
-fn acronym_ref_empty() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\acrshort{}
- |"#
- ));
-}
-
-#[test]
-fn acronym_ref_after_group() {
- assert_eq!(
- complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\acrshort{}
- |"#,
- ),
- Vec::new()
- );
-}
-
-#[test]
-fn acronym_ref_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\acrshort{f
- |
- ^"#
- ));
-}
-
-#[test]
-fn acronym_package_ref() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\acrodef{fpsLabel}[FPS]{Frames per Second}
-\ac{f
- |
- ^"#
- ));
-}
-
-#[test]
-fn glossary_ref_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\gls{f}
- |
- ^"#
- ));
-}
-
-#[test]
-fn glossary_ref_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
-\gls{f
- |
- ^"#
- ));
-}
-
-#[test]
-fn argument_empty() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{amsfonts}
-\mathbb{}
- |"#
- ));
-}
-
-#[test]
-fn argument_word() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{amsfonts}
-\mathbb{A}
- |
- ^"#
- ));
-}
-
-#[test]
-fn argument_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{amsfonts}
-\mathbb{
- |
-Test"#
- ));
-}
-
-#[test]
-fn argument_open_brace_unrelated() {
- assert_eq!(
- complete(
- r#"
-%! main.tex
-\usepackage{amsfonts}
-\mathbb{}{
- |
-Test"#,
- ),
- Vec::new()
- );
-}
-
-#[test]
-fn begin_environment_without_snippet_support() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\beg
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn citation() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{article}
-\bibliography{main}
-\begin{document}
-\cite{
- |
-\end{document}
-
-%! main.bib
-@article{foo:2019,
- author = {Foo Bar},
- title = {Baz Qux},
- year = {2019},
-}
-
-@article{bar:2005,}"#
- ));
-}
-
-#[test]
-fn citation_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\addbibresource{main.bib}
-\cite{
- |
-
-%! main.bib
-@article{foo,}"#
- ));
-}
-
-#[test]
-fn citation_open_brace_multiple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\addbibresource{main.bib}
-\cite{foo,a
- |
- ^
-
-%! main.bib
-@article{foo,}"#
- ));
-}
-
-#[test]
-fn citation_acronym() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\addbibresource{main.bib}
-\DeclareAcronym{foo}{cite={}}
- |
-
-%! main.bib
-@article{foo,}"#
- ));
-}
-
-#[test]
-fn citation_after_brace() {
- assert_eq!(
- complete(
- r#"
-%! main.tex
-\documentclass{article}
-\bibliography{main}
-\begin{document}
-\cite{}
- |
-\end{document}
-
-%! main.bib
-@article{foo,}"#,
- ),
- Vec::new()
- );
-}
-
-#[test]
-fn color_model_definition_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\definecolor{foo}{}
- |"#
- ));
-}
-
-#[test]
-fn color_model_definition_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\definecolor{foo}{
- |"#
- ));
-}
-
-#[test]
-fn color_model_definition_set_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\definecolorset{}
- |"#
- ));
-}
-
-#[test]
-fn color_model_definition_set_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\definecolorset{
- |"#
- ));
-}
-
-#[test]
-fn color_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\color{}
- |"#
- ));
-}
-
-#[test]
-fn color_word() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\color{re}
- |
- ^^"#
- ));
-}
-
-#[test]
-fn color_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\color{
- |"#
- ));
-}
-
-#[test]
-fn component_command_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\
- |"#
- ));
-}
-
-#[test]
-fn component_command_simple_before() {
- assert_eq!(
- complete(
- r#"
-%! main.tex
-\
-|"#,
- ),
- Vec::new()
- );
-}
-
-#[test]
-fn component_command_simple_package() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{lipsum}
-\lips
- |
- ^^^^"#
- ));
-}
-
-#[test]
-fn component_command_bibtex() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{b,
- c = {\LaT }
- |
- ^^^
-}"#
- ));
-}
-
-#[test]
-fn component_environment_simple() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\begin{doc
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn component_environment_simple_end() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\begin{document}
-\end{
- |"#
- ));
-}
-
-#[test]
-fn component_environment_class() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{article}
-\begin{thein}
- |
- ^^^^^"#
- ));
-}
-
-#[test]
-fn component_environment_command_definition() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newcommand{\foo}{\begin{doc}
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn entry_type_at_empty() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@
- |"#
- ));
-}
-
-#[test]
-fn entry_type_before_preamble() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@preamble
- |
- ^^^^^^^^"#
- ));
-}
-
-#[test]
-fn entry_type_before_string() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@string
- |
- ^^^^^^"#
- ));
-}
-
-#[test]
-fn entry_type_before_article() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article
- |
- ^^^^^^^"#
- ));
-}
-
-#[test]
-fn entry_type_after_preamble() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@preamble{
- |
- ^^^^^^^^"#
- ));
-}
-
-#[test]
-fn entry_type_after_string() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@string{
- |
- ^^^^^^"#
- ));
-}
-
-#[test]
-fn entry_type_complete_entry() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo, author = {foo}}
- |
- ^^^^^^^"#
- ));
-}
-
-#[test]
-fn field_empty_entry_open() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo,
- |"#
- ));
-}
-
-#[test]
-fn field_empty_entry_closed() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo,}
- |"#
- ));
-}
-
-#[test]
-fn field_entry_field_name() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo, a
- |
- ^"#
- ));
-}
-
-#[test]
-fn field_entry_two_fields_name_open() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo, author = bar, edit
- |
- ^^^^"#
- ));
-}
-
-#[test]
-fn field_entry_two_fields_name_closed() {
- assert_json_snapshot!(complete(
- r#"
-%! main.bib
-@article{foo, author = bar, edit}
- |
- ^^^^"#
- ));
-}
-
-#[test]
-fn import_package_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{lips
- |
- ^^^^"#
- ));
-}
-
-#[test]
-fn import_package_closed_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepackage{lips}
- |
- ^^^^"#
- ));
-}
-
-#[test]
-fn import_class_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{art \foo
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn import_class_closed_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{art}
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn label() {
- assert_json_snapshot!(complete(
- r#"
-%! foo.tex
-\documentclass{article}
-
-\usepackage{amsmath}
-\usepackage{caption}
-\usepackage{amsthm}
-\newtheorem{lemma}{Lemma}
-
-\begin{document}
-
-\section{Foo}%
-\label{sec:foo}
-
-\begin{equation}%
-\label{eq:foo}
- 1 + 1 = 2
-\end{equation}
-
-\begin{equation}%
-\label{eq:bar}
- 1 + 1 = 2
-\end{equation}
-
-\begin{figure}%
-\LaTeX{}
-\caption{Baz}%
-\label{fig:baz}
-\end{figure}
-
-\begin{lemma}%
-\label{thm:foo}
- 1 + 1 = 2
-\end{lemma}
-
-\include{bar}
-
-\end{document}
-
-%! bar.tex
-\section{Bar}%
-\label{sec:bar}
-
-Lorem ipsum dolor sit amet.
-\ref{}
- |
-
-%! foo.aux
-\relax
-\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
-\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
-\newlabel{fig:baz}{{1}{1}}
-\@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
-\newlabel{sec:foo}{{1}{1}}
-\newlabel{eq:foo}{{1}{1}}
-\newlabel{eq:bar}{{2}{1}}
-\newlabel{thm:foo}{{1}{1}}
-\@input{bar.aux}"#
- ));
-}
-
-#[test]
-fn theorem_begin() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newtheorem{lemma}{Lemma}
-\begin{lem
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn theorem_end() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\newtheorem{lemma}{Lemma}
-\begin{}
-\end{lem
- |
- ^^^"#
- ));
-}
-
-#[test]
-fn tikz_library_open_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepgflibrary{
- |"#
- ));
-}
-
-#[test]
-fn tikz_library_closed_brace() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\usepgflibrary{}
- |"#
- ));
-}
-
-#[test]
-fn test_user_command() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\foobar
-\fooba
- |
- ^^^^^
-\begin{foo}
-\end{foo}
-\begin{fo}"#
- ));
-}
-
-#[test]
-fn test_user_environment() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\foobar
-\fooba
-\begin{foo}
-\end{foo}
-\begin{fo}
- |
- ^^"#
- ));
-}
-
-#[test]
-fn test_project_resolution_import() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{article}
-\import{sub}{sub.tex}
-\lipsu
- |
- ^^^^^
-
-%! sub/sub.tex
-\input{child.tex}
-
-%! sub/child.tex
-\usepackage{lipsum}"#
- ));
-}
-
-#[test]
-fn test_project_resolution_texlabroot() {
- assert_json_snapshot!(complete(
- r#"
-%! src/main.tex
-\documentclass{article}
-\include{src/foo}
-\lipsu
- |
- ^^^^^
-
-%! src/foo.tex
-\include{src/bar}
-
-%! src/bar.tex
-\usepackage{lipsum}
-
-%! .texlabroot"#
- ));
-}
-
-#[test]
-fn issue_857_1() {
- assert_json_snapshot!(complete(
- r#"
-%! bug.tex
-\documentclass{article}
-\newcommand{\ö}{foo}
-\newcommand{\öö}{bar}
-\newcommand{\ööabc}{baz}
-\begin{document}
-\ö
- |
- ^
-\end{document}
-"#
- ));
-}
-
-#[test]
-fn issue_864() {
- assert_json_snapshot!(complete(
- r#"
-%! bug.tex
-\documentclass{article}
-\def\あいうえお{}
-\begin{document}
-\あ
- |
- ^
-\end{document}"#
- ))
-}
-
-#[test]
-fn issue_883() {
- assert_json_snapshot!(complete(
- r#"
-%! bug.tex
-\begin{doc
- |
- ^^^
-% Comment"#
- ))
-}
-
-#[test]
-fn issue_885() {
- assert_json_snapshot!(complete(
- r#"
-%! main.tex
-\documentclass{book}
-\usepackage{import}
-\begin{document}
-\subincludefrom{part 1}{main}
-\include{part 2/main}
-
-\ref{sec}
- |
- ^^^
-\end{document}
-
-%! part 1/main.tex
-\part{1}
-\label{part 1}
-\subimport{chapter 1}{main}
-
-%! part 1/chapter 1/main.tex
-\chapter{1}
-\label{chapter 1}
-\subimport{./}{section 1}
-%\subimport{}{section 1}
-
-%! part 1/chapter 1/section 1.tex
-\section{1}
-\label{section 1}
-
-%! part 2/main.tex
-\part{2}
-\label{part 2}
-\input{part 2/chapter 2/main}
-
-%! part 2/chapter 2/main.tex
-\chapter{2}
-\label{chapter 2}
-\input{part 2/chapter 2/section 2}
-
-%! part 2/chapter 2/section 2.tex
-\section{2}
-\label{section 2}
-"#
- ));
-}
diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/rename.rs b/support/texlab/crates/texlab/tests/lsp/text_document/rename.rs
deleted file mode 100644
index 183443809a..0000000000
--- a/support/texlab/crates/texlab/tests/lsp/text_document/rename.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use std::collections::HashMap;
-
-use lsp_types::{request::Rename, ClientCapabilities, RenameParams, TextEdit, Url, WorkspaceEdit};
-
-use crate::fixture::TestBed;
-
-fn check(fixture: &str, new_name: &str) {
- let test_bed = TestBed::new(fixture).unwrap();
-
- test_bed.initialize(ClientCapabilities::default()).unwrap();
-
- let mut expected_changes: HashMap<Url, Vec<TextEdit>> = HashMap::new();
- for location in test_bed.locations() {
- expected_changes
- .entry(location.uri.clone())
- .or_default()
- .push(TextEdit::new(location.range, new_name.to_string()));
- }
-
- let text_document_position = test_bed.cursor().unwrap();
- let actual_edit = test_bed
- .client()
- .send_request::<Rename>(RenameParams {
- text_document_position,
- new_name: new_name.to_string(),
- work_done_progress_params: Default::default(),
- })
- .unwrap()
- .unwrap_or_default();
-
- assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
-}
-
-#[test]
-fn command() {
- check(
- r#"
-%! foo.tex
-\baz
- |
- ^^^
-\include{bar.tex}
-
-%! bar.tex
-\baz
- ^^^
-"#,
- "qux",
- )
-}
-
-#[test]
-fn entry() {
- check(
- r#"
-%! main.bib
-@article{foo, bar = baz}
- |
- ^^^
-
-%! main.tex
-\addbibresource{main.bib}
-\cite{foo}
- ^^^
-"#,
- "qux",
- )
-}
-
-#[test]
-fn citation() {
- check(
- r#"
-%! main.bib
-@article{foo, bar = baz}
- ^^^
-
-%! main.tex
-\addbibresource{main.bib}
-\cite{foo}
- |
- ^^^
-"#,
- "qux",
- )
-}
-
-#[test]
-fn label() {
- check(
- r#"
-%! foo.tex
-\label{foo}\include{bar}
- |
- ^^^
-
-%! bar.tex
-\ref{foo}
- ^^^
-
-%! baz.tex
-\ref{foo}
-"#,
- "bar",
- )
-}
diff --git a/support/texlab/texlab.1 b/support/texlab/texlab.1
index 72df5cbdc8..baa354fe74 100644
--- a/support/texlab/texlab.1
+++ b/support/texlab/texlab.1
@@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
-.TH TEXLAB "1" "August 2023" "texlab 5.9.2" "User Commands"
+.TH TEXLAB "1" "September 2023" "texlab 5.10.0" "User Commands"
.SH NAME
-texlab \- manual page for texlab 5.9.2
+texlab \- manual page for texlab 5.10.0
.SH SYNOPSIS
.B texlab
[\fI\,OPTIONS\/\fR]
diff --git a/support/texlab/texlab.pdf b/support/texlab/texlab.pdf
index 528ecd8434..7c3f5b479e 100644
--- a/support/texlab/texlab.pdf
+++ b/support/texlab/texlab.pdf
Binary files differ