summaryrefslogtreecommitdiff
path: root/support/texlab
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-07-04 03:03:43 +0000
committerNorbert Preining <norbert@preining.info>2022-07-04 03:03:43 +0000
commit1c10375ec46d7d83b2f1efc2a71b7ea114c889f0 (patch)
tree47b3469111105b7767111dcb89858fbc1e73377f /support/texlab
parent34d318af65decbdb242ae03b64bf3f53266067b6 (diff)
CTAN sync 202207040303
Diffstat (limited to 'support/texlab')
-rw-r--r--support/texlab/CHANGELOG.md17
-rw-r--r--support/texlab/Cargo.lock197
-rw-r--r--support/texlab/Cargo.toml42
-rw-r--r--support/texlab/docs/options.md34
-rw-r--r--support/texlab/docs/previewing.md2
-rw-r--r--support/texlab/docs/tectonic.md2
-rw-r--r--support/texlab/src/debouncer.rs55
-rw-r--r--support/texlab/src/diagnostics.rs177
-rw-r--r--support/texlab/src/diagnostics/bibtex.rs147
-rw-r--r--support/texlab/src/diagnostics/build.rs (renamed from support/texlab/src/diagnostics/build_log.rs)44
-rw-r--r--support/texlab/src/diagnostics/chktex.rs43
-rw-r--r--support/texlab/src/diagnostics/debouncer.rs75
-rw-r--r--support/texlab/src/diagnostics/latex.rs90
-rw-r--r--support/texlab/src/features/build.rs8
-rw-r--r--support/texlab/src/features/forward_search.rs74
-rw-r--r--support/texlab/src/lib.rs3
-rw-r--r--support/texlab/src/main.rs6
-rw-r--r--support/texlab/src/options.rs80
-rw-r--r--support/texlab/src/server.rs192
-rw-r--r--support/texlab/tests/integration/dev.rs17
-rw-r--r--support/texlab/tests/integration/lsp/client.rs7
-rw-r--r--support/texlab/tests/integration/lsp/fixture.rs2
-rw-r--r--support/texlab/tests/integration/lsp/text_document.rs2
-rw-r--r--support/texlab/tests/integration/lsp/text_document/did_change_configuration.rs39
-rw-r--r--support/texlab/tests/integration/lsp/text_document/publish_diagnostics.rs264
-rw-r--r--support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__did_change_configuration__invalid_configuration.snap10
-rw-r--r--support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_allowed.snap24
-rw-r--r--support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_ignored.snap24
-rw-r--r--support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_none.snap39
-rw-r--r--support/texlab/tests/integration/main.rs1
30 files changed, 1156 insertions, 561 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md
index 5400a1b538..85f8d0bdb3 100644
--- a/support/texlab/CHANGELOG.md
+++ b/support/texlab/CHANGELOG.md
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [4.2.0] - 03.07.2022
+
+### Added
+
+- Add support for escaping placeholders in forward search ([#649](https://github.com/latex-lsp/texlab/issues/649))
+- Add support for diagnostic filtering ([#323](https://github.com/latex-lsp/texlab/issues/323))
+- Add pre-built binaries for the following targets:
+ - `aarch64-unknown-linux-gnu`
+ - `armv7-unknown-linux-gnueabihf`
+ - `x86_64-unknown-linux-musl`
+ - `aarch64-pc-windows-msvc`
+ - `i686-pc-windows-msvc`
+
+### Fixed
+
+- Parse incomplete server options correctly ([#651](https://github.com/latex-lsp/texlab/issues/651))
+
## [4.1.0] - 12.06.2022
### Added
diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock
index 8b5e64f3d7..cd1348e8c7 100644
--- a/support/texlab/Cargo.lock
+++ b/support/texlab/Cargo.lock
@@ -28,9 +28,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.57"
+version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
+checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "assert_unordered"
@@ -60,9 +60,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "beef"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736"
+checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bitflags"
@@ -105,9 +105,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.9.1"
+version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
+checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "byte-tools"
@@ -159,23 +159,23 @@ dependencies = [
[[package]]
name = "clap"
-version = "3.1.18"
+version = "3.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
+checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
dependencies = [
"bitflags",
"clap_derive",
"clap_lex",
"indexmap",
- "lazy_static",
+ "once_cell",
"textwrap 0.15.0",
]
[[package]]
name = "clap_derive"
-version = "3.1.18"
+version = "3.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
+checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
dependencies = [
"heck",
"proc-macro-error",
@@ -186,9 +186,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.2.0"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
@@ -265,9 +265,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.5.4"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
+checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -286,26 +286,26 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.8"
+version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
+checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
- "lazy_static",
"memoffset",
+ "once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
dependencies = [
"cfg-if",
- "lazy_static",
+ "once_cell",
]
[[package]]
@@ -365,9 +365,9 @@ dependencies = [
[[package]]
name = "either"
-version = "1.6.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
[[package]]
name = "encode_unicode"
@@ -469,9 +469,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
@@ -486,12 +486,6 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
-
-[[package]]
-name = "hashbrown"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
@@ -542,19 +536,19 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.7.0"
+version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
- "hashbrown 0.11.2",
+ "hashbrown",
]
[[package]]
name = "insta"
-version = "1.14.1"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcc3e639bcba360d9237acabd22014c16f3df772db463b7446cd81b070714767"
+checksum = "4126dd76ebfe2561486a1bd6738a33d2029ffb068a99ac446b7f8c77b2e58dbc"
dependencies = [
"console",
"once_cell",
@@ -614,9 +608,9 @@ checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
[[package]]
name = "js-sys"
-version = "0.3.57"
+version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
+checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
dependencies = [
"wasm-bindgen",
]
@@ -635,9 +629,9 @@ checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "linked-hash-map"
-version = "0.5.4"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
@@ -735,23 +729,14 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.5.1"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
+checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
dependencies = [
"adler",
]
[[package]]
-name = "multimap"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -800,15 +785,15 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "os_str_bytes"
-version = "6.0.1"
+version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
+checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "parking_lot"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -996,18 +981,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
-version = "1.0.39"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
+checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.18"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
@@ -1114,7 +1099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1f383129e417a6265b16ed78e6e9307748f0863b2ba75f78ff14717db5b017"
dependencies = [
"countme",
- "hashbrown 0.12.1",
+ "hashbrown",
"memoffset",
"rustc-hash",
"text-size",
@@ -1137,9 +1122,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
+checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf"
[[package]]
name = "ryu"
@@ -1164,15 +1149,15 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
+checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
[[package]]
name = "serde"
-version = "1.0.137"
+version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
dependencies = [
"serde_derive",
]
@@ -1189,9 +1174,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.137"
+version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
+checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
dependencies = [
"proc-macro2",
"quote",
@@ -1200,9 +1185,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.81"
+version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
+checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa 1.0.2",
"ryu",
@@ -1210,6 +1195,16 @@ dependencies = [
]
[[package]]
+name = "serde_regex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
+dependencies = [
+ "regex",
+ "serde",
+]
+
+[[package]]
name = "serde_repr"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1267,9 +1262,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.8.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "smol_str"
@@ -1282,18 +1277,18 @@ dependencies = [
[[package]]
name = "strum"
-version = "0.24.0"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
-version = "0.24.0"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
+checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b"
dependencies = [
"heck",
"proc-macro2",
@@ -1304,9 +1299,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.95"
+version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
+checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
@@ -1339,13 +1334,13 @@ dependencies = [
[[package]]
name = "texlab"
-version = "4.1.0"
+version = "4.2.0"
dependencies = [
"anyhow",
"assert_unordered",
"byteorder",
"chrono",
- "clap 3.1.18",
+ "clap 3.2.8",
"criterion",
"crossbeam-channel",
"dashmap",
@@ -1364,7 +1359,6 @@ dependencies = [
"logos",
"lsp-server",
"lsp-types",
- "multimap",
"once_cell",
"petgraph",
"regex",
@@ -1372,6 +1366,7 @@ dependencies = [
"rustc-hash",
"serde",
"serde_json",
+ "serde_regex",
"serde_repr",
"smol_str",
"strum",
@@ -1469,9 +1464,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "titlecase"
-version = "1.1.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f565e410cfc24c2f2a89960b023ca192689d7f77d3f8d4f4af50c2d8affe1117"
+checksum = "ea7a2f1a76e892e6c07c275e6e851400257304a6e34c0e4e32ebbd3871808802"
dependencies = [
"lazy_static",
"regex",
@@ -1497,15 +1492,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-normalization"
-version = "0.1.19"
+version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
dependencies = [
"tinyvec",
]
@@ -1569,15 +1564,15 @@ dependencies = [
[[package]]
name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.80"
+version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
+checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -1585,9 +1580,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.80"
+version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
+checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
dependencies = [
"bumpalo",
"lazy_static",
@@ -1600,9 +1595,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.80"
+version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
+checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1610,9 +1605,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.80"
+version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
+checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
dependencies = [
"proc-macro2",
"quote",
@@ -1623,15 +1618,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.80"
+version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
+checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
[[package]]
name = "web-sys"
-version = "0.3.57"
+version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
+checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
dependencies = [
"js-sys",
"wasm-bindgen",
diff --git a/support/texlab/Cargo.toml b/support/texlab/Cargo.toml
index f85c0f31ed..803f280e39 100644
--- a/support/texlab/Cargo.toml
+++ b/support/texlab/Cargo.toml
@@ -1,12 +1,12 @@
[package]
name = "texlab"
description = "LaTeX Language Server"
-version = "4.1.0"
+version = "4.2.0"
license = "GPL-3.0"
readme = "README.md"
authors = [
- "Eric Förster <eric.foerster@outlook.com>",
- "Patrick Förster <patrick.foerster@outlook.de>",
+ "Eric Förster <eric.foerster@outlook.com>",
+ "Patrick Förster <patrick.foerster@outlook.de>",
]
edition = "2021"
rust-version = "1.58.1"
@@ -16,14 +16,14 @@ documentation = "https://github.com/latex-lsp/texlab"
keywords = ["lsp", "server", "latex", "bibtex"]
categories = ["development-tools"]
exclude = [
- ".gitattributes",
- ".gitignore",
- ".github/**",
- "tests/it/**",
- "*.snap",
- "texlab.1",
- "texlab.pdf",
- "texlab.tex",
+ ".gitattributes",
+ ".gitignore",
+ ".github/**",
+ "tests/it/**",
+ "*.snap",
+ "texlab.1",
+ "texlab.pdf",
+ "texlab.tex",
]
[[bin]]
@@ -38,14 +38,14 @@ default = ["completion"]
completion = ["fuzzy-matcher"]
[dependencies]
-anyhow = "1.0.57"
+anyhow = "1.0.58"
byteorder = "1.4.3"
chrono = { version = "0.4.19", default-features = false, features = ["std"] }
-clap = { version = "3.1.17", features = [
- "std",
- "derive",
+clap = { version = "3.2.8", features = [
+ "std",
+ "derive",
], default-features = false }
-crossbeam-channel = "0.5.4"
+crossbeam-channel = "0.5.5"
dashmap = "~5.2.0"
derive_more = "0.99.17"
encoding_rs = "0.8.31"
@@ -60,28 +60,28 @@ log = "0.4.17"
logos = "0.12.1"
lsp-server = "0.6.0"
lsp-types = "0.93.0"
-multimap = "0.8.3"
once_cell = "1.10.0"
petgraph = "0.6.2"
regex = "1.5.5"
rowan = "0.15.5"
rustc-hash = "1.1.0"
serde = "1.0.137"
-serde_json = "1.0.81"
+serde_json = "1.0.82"
+serde_regex = "1.1.0"
serde_repr = "0.1.8"
smol_str = { version = "0.1.23", features = ["serde"] }
strum = { version = "0.24.0", features = ["derive"] }
tempfile = "3.3.0"
threadpool = "1.8.1"
-titlecase = "1.1.0"
-unicode-normalization = "0.1.19"
+titlecase = "2.0.0"
+unicode-normalization = "0.1.20"
url = { version = "2.2.2", features = ["serde"] }
uuid = { version = "1.1.2", features = ["v4"] }
[dev-dependencies]
assert_unordered = "0.3.5"
criterion = { version = "0.3.5" }
-insta = { version = "1.14.1", features = ["backtrace", "redactions"] }
+insta = { version = "1.15.0", features = ["backtrace", "redactions"] }
jod-thread = "0.1.2"
[profile.release]
diff --git a/support/texlab/docs/options.md b/support/texlab/docs/options.md
index e1a69c4996..9b3599cce1 100644
--- a/support/texlab/docs/options.md
+++ b/support/texlab/docs/options.md
@@ -77,6 +77,8 @@ In this case, use the `-outdir` flag for `latexmk`.
**Default value:** `.` (the same directory as the TeX file)
+---
+
## texlab.forwardSearch.executable
Defines the executable of the PDF previewer.
@@ -135,6 +137,38 @@ Delay in milliseconds before reporting diagnostics.
---
+## texlab.diagnostics.allowedPatterns
+
+A list of regular expressions used to filter the list of reported diagnostics.
+If specified, only diagnostics that match _at least one_ of the specified patterns
+are sent to the client.
+
+See also [`texlab.diagnostics.ignoredPatterns`](#texlabdiagnosticsignoredpatterns).
+
+_Hint_:
+If both `allowedPatterns` and `ignoredPatterns` are set,
+then allowed patterns are applied first. Afterwards, the results are filtered with the ignored patterns.
+
+**Type:** `RegExp[]`
+
+**Default value:** `[]`
+
+---
+
+## texlab.diagnostics.ignoredPatterns
+
+A list of regular expressions used to filter the list of reported diagnostics.
+If specified, only diagnostics that match _none_ of the specified patterns
+are sent to the client.
+
+See also [`texlab.diagnostics.allowedPatterns`](#texlabdiagnosticsallowedpatterns).
+
+**Type:** `RegExp[]`
+
+**Default value:** `[]`
+
+---
+
## texlab.formatterLineLength
Defines the maximum amount of characters per line (0 = disable) when formatting BibTeX files.
diff --git a/support/texlab/docs/previewing.md b/support/texlab/docs/previewing.md
index 6c667e0fd0..3e97390429 100644
--- a/support/texlab/docs/previewing.md
+++ b/support/texlab/docs/previewing.md
@@ -97,7 +97,7 @@ Add the following lines to your editor config:
```json
{
"texlab.forwardSearch.executable": "okular",
- "texlab.forwardSearch.args": ["--unique", "file:%p#src:%l%f"]
+ "texlab.forwardSearch.args": ["--editor-cmd": "code -g \"%%f\":%%l", "--unique", "file:%p#src:%l%f"]
}
```
diff --git a/support/texlab/docs/tectonic.md b/support/texlab/docs/tectonic.md
index e75fd4a3ee..b08e64b946 100644
--- a/support/texlab/docs/tectonic.md
+++ b/support/texlab/docs/tectonic.md
@@ -24,7 +24,7 @@ Without the `--keep-logs` flag, `texlab` won't be able to report compilation war
"texlab.build.executable": "tectonic",
"texlab.build.args": [
"-X",
- "compile"
+ "compile",
"%f",
"--synctex",
"--keep-logs",
diff --git a/support/texlab/src/debouncer.rs b/support/texlab/src/debouncer.rs
new file mode 100644
index 0000000000..b123dfc433
--- /dev/null
+++ b/support/texlab/src/debouncer.rs
@@ -0,0 +1,55 @@
+use std::time::{Duration, Instant};
+
+use anyhow::Result;
+
+pub struct Sender<T> {
+ tx: crossbeam_channel::Sender<(T, crossbeam_channel::Receiver<Instant>)>,
+}
+
+impl<T> Clone for Sender<T> {
+ fn clone(&self) -> Self {
+ Self {
+ tx: self.tx.clone(),
+ }
+ }
+}
+
+impl<T> Sender<T>
+where
+ T: Send + Sync + 'static,
+{
+ pub fn send(&self, msg: T, delay: Duration) -> Result<()> {
+ self.tx.send((msg, crossbeam_channel::after(delay)))?;
+ Ok(())
+ }
+}
+
+pub struct Receiver<T> {
+ rx: crossbeam_channel::Receiver<(T, crossbeam_channel::Receiver<Instant>)>,
+}
+
+impl<T> Clone for Receiver<T> {
+ fn clone(&self) -> Self {
+ Self {
+ rx: self.rx.clone(),
+ }
+ }
+}
+
+impl<T> Receiver<T> {
+ pub fn recv(&self) -> Result<T> {
+ let (mut last_msg, delay) = self.rx.recv()?;
+ delay.recv()?;
+ while let Ok((msg, delay)) = self.rx.try_recv() {
+ delay.recv()?;
+ last_msg = msg;
+ }
+
+ Ok(last_msg)
+ }
+}
+
+pub fn unbounded<T>() -> (Sender<T>, Receiver<T>) {
+ let (tx, rx) = crossbeam_channel::unbounded();
+ (Sender { tx }, Receiver { rx })
+}
diff --git a/support/texlab/src/diagnostics.rs b/support/texlab/src/diagnostics.rs
index 5ce177b015..bdd8faf106 100644
--- a/support/texlab/src/diagnostics.rs
+++ b/support/texlab/src/diagnostics.rs
@@ -1,56 +1,167 @@
mod bibtex;
-mod build_log;
+mod build;
mod chktex;
-mod debouncer;
mod latex;
use std::sync::Arc;
-use lsp_types::{Diagnostic, Url};
-use multimap::MultiMap;
-use rustc_hash::FxHashMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, NumberOrString, Range, Url};
+use regex::Regex;
-use crate::{Options, Workspace};
-
-pub use self::debouncer::{DiagnosticsDebouncer, DiagnosticsMessage};
+use crate::Workspace;
use self::{
- bibtex::analyze_bibtex_static, build_log::analyze_build_log_static,
- chktex::analyze_latex_chktex, latex::analyze_latex_static,
+ bibtex::collect_bibtex_diagnostics, build::collect_build_diagnostics,
+ chktex::collect_chktex_diagnostics, latex::collect_latex_diagnostics,
};
-#[derive(Default)]
-pub struct DiagnosticsManager {
- static_diagnostics: FxHashMap<Arc<Url>, MultiMap<Arc<Url>, Diagnostic>>,
- chktex_diagnostics: MultiMap<Arc<Url>, Diagnostic>,
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Diagnostic {
+ pub severity: DiagnosticSeverity,
+ pub range: Range,
+ pub code: DiagnosticCode,
+ pub message: String,
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
+pub enum DiagnosticCode {
+ Latex(LatexCode),
+ Bibtex(BibtexCode),
+ Chktex(String),
+ Build(Arc<Url>),
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+pub enum LatexCode {
+ UnexpectedRCurly,
+ RCurlyInserted,
+ MismatchedEnvironment,
+}
+
+impl From<LatexCode> for String {
+ fn from(code: LatexCode) -> Self {
+ match code {
+ LatexCode::UnexpectedRCurly => "Unexpected \"}\"".to_string(),
+ LatexCode::RCurlyInserted => "Missing \"}\" inserted".to_string(),
+ LatexCode::MismatchedEnvironment => "Mismatched environment".to_string(),
+ }
+ }
}
-impl DiagnosticsManager {
- pub fn update_static(&mut self, workspace: &Workspace, uri: Arc<Url>) {
- let mut diagnostics_by_uri = MultiMap::new();
- analyze_build_log_static(workspace, &mut diagnostics_by_uri, &uri);
- analyze_bibtex_static(workspace, &mut diagnostics_by_uri, &uri);
- analyze_latex_static(workspace, &mut diagnostics_by_uri, &uri);
- self.static_diagnostics.insert(uri, diagnostics_by_uri);
+impl From<LatexCode> for NumberOrString {
+ fn from(code: LatexCode) -> Self {
+ match code {
+ LatexCode::UnexpectedRCurly => NumberOrString::Number(1),
+ LatexCode::RCurlyInserted => NumberOrString::Number(2),
+ LatexCode::MismatchedEnvironment => NumberOrString::Number(3),
+ }
}
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+#[allow(clippy::enum_variant_names)]
+pub enum BibtexCode {
+ ExpectingLCurly,
+ ExpectingKey,
+ ExpectingRCurly,
+ ExpectingEq,
+ ExpectingFieldValue,
+}
- pub fn update_chktex(&mut self, workspace: &Workspace, uri: &Url, options: &Options) {
- analyze_latex_chktex(workspace, &mut self.chktex_diagnostics, uri, options);
+impl From<BibtexCode> for String {
+ fn from(code: BibtexCode) -> Self {
+ match code {
+ BibtexCode::ExpectingLCurly => "Expecting a curly bracket: \"{\"".to_string(),
+ BibtexCode::ExpectingKey => "Expecting a key".to_string(),
+ BibtexCode::ExpectingRCurly => "Expecting a curly bracket: \"}\"".to_string(),
+ BibtexCode::ExpectingEq => "Expecting an equality sign: \"=\"".to_string(),
+ BibtexCode::ExpectingFieldValue => "Expecting a field value".to_string(),
+ }
}
+}
- #[must_use]
- pub fn publish(&self, uri: &Url) -> Vec<Diagnostic> {
- let mut all_diagnostics = Vec::new();
- for diagnostics_by_uri in self.static_diagnostics.values() {
- if let Some(diagnostics) = diagnostics_by_uri.get_vec(uri) {
- all_diagnostics.append(&mut diagnostics.clone());
- }
+impl From<BibtexCode> for NumberOrString {
+ fn from(code: BibtexCode) -> Self {
+ match code {
+ BibtexCode::ExpectingLCurly => NumberOrString::Number(4),
+ BibtexCode::ExpectingKey => NumberOrString::Number(5),
+ BibtexCode::ExpectingRCurly => NumberOrString::Number(6),
+ BibtexCode::ExpectingEq => NumberOrString::Number(7),
+ BibtexCode::ExpectingFieldValue => NumberOrString::Number(8),
}
+ }
+}
+
+#[derive(Default, Clone)]
+pub struct DiagnosticManager {
+ all_diagnostics: Arc<DashMap<Arc<Url>, Vec<Diagnostic>>>,
+}
+
+impl DiagnosticManager {
+ pub fn push_syntax(&self, workspace: &Workspace, uri: &Url) {
+ collect_bibtex_diagnostics(&self.all_diagnostics, workspace, uri)
+ .or_else(|| collect_latex_diagnostics(&self.all_diagnostics, workspace, uri))
+ .or_else(|| collect_build_diagnostics(&self.all_diagnostics, workspace, uri));
+ }
+
+ pub fn push_chktex(&self, workspace: &Workspace, uri: &Url) {
+ collect_chktex_diagnostics(&self.all_diagnostics, workspace, uri);
+ }
- if let Some(diagnostics) = self.chktex_diagnostics.get_vec(uri) {
- all_diagnostics.append(&mut diagnostics.clone());
+ pub fn publish(&self, workspace: &Workspace, uri: &Url) -> Vec<lsp_types::Diagnostic> {
+ let options = &workspace.environment.options.diagnostics;
+
+ let mut results = Vec::new();
+ if let Some(diagnostics) = self.all_diagnostics.get(uri) {
+ for diagnostic in diagnostics.iter() {
+ if !options.allowed_patterns.is_empty()
+ && !options
+ .allowed_patterns
+ .iter()
+ .any(|pattern| pattern.0.is_match(&diagnostic.message))
+ {
+ continue;
+ }
+
+ if options
+ .ignored_patterns
+ .iter()
+ .any(|pattern| pattern.0.is_match(&diagnostic.message))
+ {
+ continue;
+ }
+
+ let source = match diagnostic.code {
+ DiagnosticCode::Latex(_) | DiagnosticCode::Bibtex(_) => "texlab",
+ DiagnosticCode::Chktex(_) => "chktex",
+ DiagnosticCode::Build(_) => "latex-build",
+ };
+
+ let code = match diagnostic.code.clone() {
+ DiagnosticCode::Latex(code) => Some(code.into()),
+ DiagnosticCode::Bibtex(code) => Some(code.into()),
+ DiagnosticCode::Chktex(code) => Some(NumberOrString::String(code)),
+ DiagnosticCode::Build(_) => None,
+ };
+
+ results.push(lsp_types::Diagnostic {
+ range: diagnostic.range,
+ code,
+ severity: Some(diagnostic.severity),
+ message: diagnostic.message.clone(),
+ source: Some(source.to_string()),
+ ..Default::default()
+ });
+ }
}
- all_diagnostics
+ results
}
}
+
+#[derive(Debug, Default)]
+pub struct DiagnosticFilter {
+ pub allowed_patterns: Vec<Regex>,
+ pub ignored_patterns: Vec<Regex>,
+}
diff --git a/support/texlab/src/diagnostics/bibtex.rs b/support/texlab/src/diagnostics/bibtex.rs
index 1beec52c5c..42e215a794 100644
--- a/support/texlab/src/diagnostics/bibtex.rs
+++ b/support/texlab/src/diagnostics/bibtex.rs
@@ -1,7 +1,7 @@
use std::sync::Arc;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Url};
use rowan::{ast::AstNode, TextRange};
use crate::{
@@ -9,85 +9,84 @@ use crate::{
Document, LineIndexExt, Workspace,
};
-pub fn analyze_bibtex_static(
+use super::{BibtexCode, Diagnostic, DiagnosticCode};
+
+pub fn collect_bibtex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
let data = document.data.as_bibtex()?;
- for node in bibtex::SyntaxNode::new_root(data.green.clone()).descendants() {
- analyze_entry(document, diagnostics_by_uri, node.clone())
- .or_else(|| analyze_field(document, diagnostics_by_uri, node));
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Bibtex(_)));
+ diagnostics
+ });
+
+ let root = bibtex::SyntaxNode::new_root(data.green.clone());
+ for node in root.descendants() {
+ analyze_entry(all_diagnostics, document, node.clone())
+ .or_else(|| analyze_field(all_diagnostics, document, node));
}
Some(())
}
fn analyze_entry(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: bibtex::SyntaxNode,
) -> Option<()> {
let entry = bibtex::Entry::cast(node)?;
if entry.left_delim_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingLCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(entry.type_token()?.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(4)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a curly bracket: \"{\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if entry.name_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingKey;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(entry.left_delim_token()?.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(5)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a key".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if entry.right_delim_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingRCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(TextRange::empty(entry.syntax().text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(6)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a curly bracket: \"}\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
@@ -95,48 +94,44 @@ fn analyze_entry(
}
fn analyze_field(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: bibtex::SyntaxNode,
) -> Option<()> {
let field = bibtex::Field::cast(node)?;
if field.eq_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingEq;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
- .line_col_lsp_range(TextRange::empty(field.name_token()?.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(7)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting an equality sign: \"=\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ .line_col_lsp_range(field.name_token()?.text_range()),
+
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if field.value().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingFieldValue;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
- .line_col_lsp_range(TextRange::empty(field.eq_token()?.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(8)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a field value".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ .line_col_lsp_range(field.name_token()?.text_range()),
+
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
diff --git a/support/texlab/src/diagnostics/build_log.rs b/support/texlab/src/diagnostics/build.rs
index e280d862bc..4b4ffcc8b4 100644
--- a/support/texlab/src/diagnostics/build_log.rs
+++ b/support/texlab/src/diagnostics/build.rs
@@ -1,20 +1,29 @@
use std::{path::PathBuf, sync::Arc};
-use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Position, Range, Url};
use crate::{syntax::build_log::BuildErrorLevel, Workspace};
-pub fn analyze_build_log_static(
+use super::{Diagnostic, DiagnosticCode};
+
+pub fn collect_build_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
build_log_uri: &Url,
) -> Option<()> {
let build_log_document = workspace.documents_by_uri.get(build_log_uri)?;
- let parse = build_log_document.data.as_build_log()?;
+ let build_log = build_log_document.data.as_build_log()?;
+
+ all_diagnostics.alter_all(|_, mut diagnostics| {
+ diagnostics.retain(
+ |diag| !matches!(&diag.code, DiagnosticCode::Build(uri) if uri.as_ref() == build_log_uri),
+ );
+ diagnostics
+ });
let root_document = workspace.documents_by_uri.values().find(|document| {
- if let Some(data) = document.data.as_latex() {
+ document.data.as_latex().map_or(false, |data| {
!document.uri.as_str().ends_with(".aux")
&& data
.extras
@@ -22,30 +31,22 @@ pub fn analyze_build_log_static(
.log
.iter()
.any(|u| u.as_ref() == build_log_uri)
- } else {
- false
- }
+ })
})?;
let base_path = PathBuf::from(root_document.uri.path());
-
- for error in &parse.errors {
- let pos = Position::new(error.line.unwrap_or(0), 0);
+ for error in &build_log.errors {
+ let position = Position::new(error.line.unwrap_or(0), 0);
let severity = match error.level {
BuildErrorLevel::Error => DiagnosticSeverity::ERROR,
BuildErrorLevel::Warning => DiagnosticSeverity::WARNING,
};
- let range = Range::new(pos, pos);
+ let range = Range::new(position, position);
let diagnostic = Diagnostic {
+ severity,
range,
- severity: Some(severity),
- code: None,
- code_description: None,
- source: Some("latex".into()),
+ code: DiagnosticCode::Build(Arc::clone(&build_log_document.uri)),
message: error.message.clone(),
- related_information: None,
- tags: None,
- data: None,
};
let full_path = base_path.join(&error.relative_path);
@@ -60,7 +61,8 @@ pub fn analyze_build_log_static(
Arc::clone(&root_document.uri)
};
- diagnostics_by_uri.insert(uri, diagnostic);
+ all_diagnostics.entry(uri).or_default().push(diagnostic);
}
+
Some(())
}
diff --git a/support/texlab/src/diagnostics/chktex.rs b/support/texlab/src/diagnostics/chktex.rs
index 6b6405d975..ad676bd70d 100644
--- a/support/texlab/src/diagnostics/chktex.rs
+++ b/support/texlab/src/diagnostics/chktex.rs
@@ -5,24 +5,32 @@ use std::{
sync::Arc,
};
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Range, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Range, Url};
use once_cell::sync::Lazy;
use regex::Regex;
use tempfile::tempdir;
-use crate::{Options, RangeExt, Workspace};
+use crate::{RangeExt, Workspace};
-pub fn analyze_latex_chktex(
+use super::{Diagnostic, DiagnosticCode};
+
+pub fn collect_chktex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
- options: &Options,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
document.data.as_latex()?;
- let current_dir = options
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Chktex(_)));
+ diagnostics
+ });
+
+ let current_dir = workspace
+ .environment
+ .options
.root_directory
.as_ref()
.cloned()
@@ -40,15 +48,15 @@ pub fn analyze_latex_chktex(
})
.unwrap_or_else(|| ".".into());
- diagnostics_by_uri.remove(uri);
- diagnostics_by_uri.insert_many(
- Arc::clone(&document.uri),
- lint(&document.text, &current_dir).unwrap_or_default(),
- );
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .extend(lint(&document.text, &current_dir).unwrap_or_default());
+
Some(())
}
-pub static LINE_REGEX: Lazy<Regex> =
+static LINE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new("(\\d+):(\\d+):(\\d+):(\\w+):(\\w+):(.*)").unwrap());
fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> {
@@ -85,14 +93,9 @@ fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> {
diagnostics.push(Diagnostic {
range,
- severity: Some(severity),
- code: Some(NumberOrString::String(code.into())),
- code_description: None,
- source: Some("chktex".into()),
+ severity,
+ code: DiagnosticCode::Chktex(code.into()),
message,
- related_information: None,
- tags: None,
- data: None,
});
}
diff --git a/support/texlab/src/diagnostics/debouncer.rs b/support/texlab/src/diagnostics/debouncer.rs
deleted file mode 100644
index 4c83636856..0000000000
--- a/support/texlab/src/diagnostics/debouncer.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use std::{
- sync::Arc,
- thread::{self, JoinHandle},
- time::{Duration, Instant},
-};
-
-use crossbeam_channel::Sender;
-use dashmap::DashMap;
-use lsp_types::Url;
-
-use crate::{Document, Workspace};
-
-pub enum DiagnosticsMessage {
- Analyze {
- workspace: Workspace,
- document: Document,
- },
- Shutdown,
-}
-
-pub struct DiagnosticsDebouncer {
- pub sender: Sender<DiagnosticsMessage>,
- handle: Option<JoinHandle<()>>,
-}
-
-impl DiagnosticsDebouncer {
- pub fn launch<A>(action: A) -> Self
- where
- A: Fn(Workspace, Document) + Send + Clone + 'static,
- {
- let (sender, receiver) = crossbeam_channel::unbounded();
-
- let handle = thread::spawn(move || {
- let pool = threadpool::Builder::new().build();
- let last_task_time_by_uri: Arc<DashMap<Arc<Url>, Instant>> = Arc::default();
- while let Ok(DiagnosticsMessage::Analyze {
- workspace,
- document,
- }) = receiver.recv()
- {
- let delay = workspace
- .environment
- .options
- .diagnostics_delay
- .unwrap_or(300);
-
- if let Some(time) = last_task_time_by_uri.get(&document.uri) {
- if time.elapsed().as_millis() < u128::from(delay) {
- continue;
- }
- }
-
- let last_task_time_by_uri = Arc::clone(&last_task_time_by_uri);
- let action = action.clone();
- pool.execute(move || {
- thread::sleep(Duration::from_millis(delay));
- last_task_time_by_uri.insert(Arc::clone(&document.uri), Instant::now());
- action(workspace, document);
- });
- }
- });
-
- Self {
- sender,
- handle: Some(handle),
- }
- }
-}
-
-impl Drop for DiagnosticsDebouncer {
- fn drop(&mut self) {
- self.sender.send(DiagnosticsMessage::Shutdown).unwrap();
- self.handle.take().unwrap().join().unwrap();
- }
-}
diff --git a/support/texlab/src/diagnostics/latex.rs b/support/texlab/src/diagnostics/latex.rs
index 2c271ad54a..a17aab3f77 100644
--- a/support/texlab/src/diagnostics/latex.rs
+++ b/support/texlab/src/diagnostics/latex.rs
@@ -1,14 +1,16 @@
use std::sync::Arc;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Url};
use rowan::{ast::AstNode, NodeOrToken, TextRange};
use crate::{syntax::latex, Document, LineIndexExt, Workspace};
-pub fn analyze_latex_static(
+use super::{Diagnostic, DiagnosticCode, LatexCode};
+
+pub fn collect_latex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
@@ -18,25 +20,27 @@ pub fn analyze_latex_static(
let data = document.data.as_latex()?;
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Latex(_)));
+ diagnostics
+ });
+
for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
- analyze_environment(document, diagnostics_by_uri, node.clone())
- .or_else(|| analyze_curly_group(document, diagnostics_by_uri, &node))
+ analyze_environment(all_diagnostics, document, node.clone())
+ .or_else(|| analyze_curly_group(all_diagnostics, document, &node))
.or_else(|| {
if node.kind() == latex::ERROR && node.first_token()?.text() == "}" {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::UnexpectedRCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document.line_index.line_col_lsp_range(node.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(1)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Unexpected \"}\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
+
Some(())
} else {
None
@@ -48,37 +52,33 @@ pub fn analyze_latex_static(
}
fn analyze_environment(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: latex::SyntaxNode,
) -> Option<()> {
let environment = latex::Environment::cast(node)?;
let name1 = environment.begin()?.name()?.key()?;
let name2 = environment.end()?.name()?.key()?;
if name1 != name2 {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::MismatchedEnvironment;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(latex::small_range(&name1)),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(3)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Mismatched environment".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
}
Some(())
}
fn analyze_curly_group(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: &latex::SyntaxNode,
) -> Option<()> {
if !matches!(
@@ -108,22 +108,18 @@ fn analyze_curly_group(
.filter_map(NodeOrToken::into_token)
.any(|token| token.kind() == latex::R_CURLY)
{
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::RCurlyInserted;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(TextRange::empty(node.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(2)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Missing \"}\" inserted".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
}
Some(())
diff --git a/support/texlab/src/features/build.rs b/support/texlab/src/features/build.rs
index ae465590fc..d961052061 100644
--- a/support/texlab/src/features/build.rs
+++ b/support/texlab/src/features/build.rs
@@ -164,12 +164,12 @@ impl BuildEngine {
let args: Vec<_> = options
.build
- .args()
- .into_iter()
- .map(|arg| replace_placeholder(arg, &path))
+ .args
+ .iter()
+ .map(|arg| replace_placeholder(arg.clone(), &path))
.collect();
- let mut process = Command::new(options.build.executable())
+ let mut process = Command::new(&options.build.executable)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
diff --git a/support/texlab/src/features/forward_search.rs b/support/texlab/src/features/forward_search.rs
index 521cc31a59..54629d0dc0 100644
--- a/support/texlab/src/features/forward_search.rs
+++ b/support/texlab/src/features/forward_search.rs
@@ -86,6 +86,73 @@ pub fn execute_forward_search(
Some(ForwardSearchResult { status })
}
+/// Iterate overs chunks of a string. Either returns a slice of the
+/// original string, or the placeholder replacement.
+pub struct PlaceHolderIterator<'a> {
+ remainder: &'a str,
+ tex_file: &'a str,
+ pdf_file: &'a str,
+ line_number: &'a str,
+}
+
+impl<'a> PlaceHolderIterator<'a> {
+ pub fn new(s: &'a str, tex_file: &'a str, pdf_file: &'a str, line_number: &'a str) -> Self {
+ Self {
+ remainder: s,
+ tex_file,
+ pdf_file,
+ line_number,
+ }
+ }
+
+ pub fn yield_remainder(&mut self) -> Option<&'a str> {
+ let chunk = self.remainder;
+ self.remainder = "";
+ Some(chunk)
+ }
+
+ pub fn yield_placeholder(&mut self) -> Option<&'a str> {
+ if self.remainder.len() >= 2 {
+ let placeholder = self.remainder;
+ self.remainder = &self.remainder[2..];
+ match &placeholder[1..2] {
+ "f" => Some(self.tex_file),
+ "p" => Some(self.pdf_file),
+ "l" => Some(self.line_number),
+ "%" => Some("%"), // escape %
+ _ => Some(&placeholder[0..2]),
+ }
+ } else {
+ self.remainder = &self.remainder[1..];
+ Some("%")
+ }
+ }
+
+ pub fn yield_str(&mut self, end: usize) -> Option<&'a str> {
+ let chunk = &self.remainder[..end];
+ self.remainder = &self.remainder[end..];
+ Some(chunk)
+ }
+}
+
+impl<'a> Iterator for PlaceHolderIterator<'a> {
+ type Item = &'a str;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ return if self.remainder.is_empty() {
+ None
+ } else if self.remainder.starts_with('%') {
+ self.yield_placeholder()
+ } else {
+ // yield up to the next % or to the end
+ match self.remainder.find('%') {
+ None => self.yield_remainder(),
+ Some(end) => self.yield_str(end),
+ }
+ };
+ }
+}
+
fn replace_placeholder(
tex_file: &Path,
pdf_file: &Path,
@@ -95,10 +162,9 @@ fn replace_placeholder(
let result = if argument.starts_with('"') || argument.ends_with('"') {
argument.to_string()
} else {
- argument
- .replace("%f", tex_file.to_str()?)
- .replace("%p", pdf_file.to_str()?)
- .replace("%l", &(line_number + 1).to_string())
+ let line = &(line_number + 1).to_string();
+ let it = PlaceHolderIterator::new(argument, tex_file.to_str()?, pdf_file.to_str()?, line);
+ it.collect::<Vec<&str>>().join("")
};
Some(result)
}
diff --git a/support/texlab/src/lib.rs b/support/texlab/src/lib.rs
index ce52bee1da..1f3d22d7aa 100644
--- a/support/texlab/src/lib.rs
+++ b/support/texlab/src/lib.rs
@@ -2,7 +2,8 @@ mod capabilities;
pub mod citation;
mod client;
pub mod component_db;
-pub mod diagnostics;
+mod debouncer;
+mod diagnostics;
mod dispatch;
pub mod distro;
mod document;
diff --git a/support/texlab/src/main.rs b/support/texlab/src/main.rs
index 71338f1bcd..06cbba3f5d 100644
--- a/support/texlab/src/main.rs
+++ b/support/texlab/src/main.rs
@@ -1,7 +1,7 @@
use std::{env, fs::OpenOptions, io, path::PathBuf};
use anyhow::Result;
-use clap::Parser;
+use clap::{ArgAction, Parser};
use log::LevelFilter;
use lsp_server::Connection;
use texlab::Server;
@@ -11,7 +11,7 @@ use texlab::Server;
#[clap(version)]
struct Opts {
/// Increase message verbosity (-vvvv for max verbosity)
- #[clap(short, long, parse(from_occurrences))]
+ #[clap(short, long, action = ArgAction::Count)]
verbosity: u8,
/// No output printed to stderr
@@ -19,7 +19,7 @@ struct Opts {
quiet: bool,
/// Write the logging output to FILE
- #[clap(long, name = "FILE", parse(from_os_str))]
+ #[clap(long, name = "FILE", value_parser)]
log_file: Option<PathBuf>,
/// Print version information and exit
diff --git a/support/texlab/src/options.rs b/support/texlab/src/options.rs
index fe0d81c639..241126d908 100644
--- a/support/texlab/src/options.rs
+++ b/support/texlab/src/options.rs
@@ -1,12 +1,15 @@
use std::path::PathBuf;
+use regex::Regex;
use serde::{Deserialize, Serialize};
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Options {
+ #[serde(default)]
pub root_directory: Option<PathBuf>,
+ #[serde(default)]
pub aux_directory: Option<PathBuf>,
#[serde(default)]
@@ -15,9 +18,14 @@ pub struct Options {
#[serde(default)]
pub latex_formatter: LatexFormatter,
+ #[serde(default)]
pub formatter_line_length: Option<i32>,
- pub diagnostics_delay: Option<u64>,
+ #[serde(default)]
+ pub diagnostics: DiagnosticsOptions,
+
+ #[serde(default = "default_diagnostics_delay")]
+ pub diagnostics_delay: u64,
#[serde(default)]
pub build: BuildOptions,
@@ -32,6 +40,10 @@ pub struct Options {
pub forward_search: ForwardSearchOptions,
}
+fn default_diagnostics_delay() -> u64 {
+ 300
+}
+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BibtexFormatter {
@@ -61,6 +73,7 @@ impl Default for LatexFormatter {
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LatexindentOptions {
+ #[serde(default)]
pub local: Option<String>,
#[serde(default)]
@@ -70,9 +83,11 @@ pub struct LatexindentOptions {
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildOptions {
- pub executable: Option<String>,
+ #[serde(default = "default_build_executable")]
+ pub executable: String,
- pub args: Option<Vec<String>>,
+ #[serde(default = "default_build_args")]
+ pub args: Vec<String>,
#[serde(default)]
pub is_continuous: bool,
@@ -84,39 +99,17 @@ pub struct BuildOptions {
pub forward_search_after: bool,
}
-impl BuildOptions {
- #[must_use]
- pub fn executable(&self) -> String {
- self.executable
- .as_ref()
- .map_or_else(|| "latexmk".to_string(), Clone::clone)
- }
-
- #[must_use]
- pub fn args(&self) -> Vec<String> {
- self.args.as_ref().map_or_else(
- || {
- vec![
- "-pdf".to_string(),
- "-interaction=nonstopmode".to_string(),
- "-synctex=1".to_string(),
- "%f".to_string(),
- ]
- },
- Clone::clone,
- )
- }
+fn default_build_executable() -> String {
+ "latexmk".to_string()
}
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ViewerOptions {
- #[serde(default)]
- pub enabled: bool,
-
- pub executable: Option<String>,
-
- pub args: Option<String>,
+fn default_build_args() -> Vec<String> {
+ vec![
+ "-pdf".to_string(),
+ "-interaction=nonstopmode".to_string(),
+ "-synctex=1".to_string(),
+ "%f".to_string(),
+ ]
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
@@ -130,7 +123,24 @@ pub struct ChktexOptions {
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct ForwardSearchOptions {
+ #[serde(default)]
pub executable: Option<String>,
+
+ #[serde(default)]
pub args: Option<Vec<String>>,
}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DiagnosticsOptions {
+ #[serde(default)]
+ pub allowed_patterns: Vec<DiagnosticsPattern>,
+
+ #[serde(default)]
+ pub ignored_patterns: Vec<DiagnosticsPattern>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct DiagnosticsPattern(#[serde(with = "serde_regex")] pub Regex);
diff --git a/support/texlab/src/server.rs b/support/texlab/src/server.rs
index f3d2bf44f4..0d5d1f8fe1 100644
--- a/support/texlab/src/server.rs
+++ b/support/texlab/src/server.rs
@@ -1,6 +1,7 @@
use std::{
path::PathBuf,
sync::{Arc, Mutex},
+ time::Duration,
};
use anyhow::Result;
@@ -13,7 +14,8 @@ use threadpool::ThreadPool;
use crate::{
client::{send_notification, send_request},
- diagnostics::{DiagnosticsDebouncer, DiagnosticsManager, DiagnosticsMessage},
+ debouncer,
+ diagnostics::DiagnosticManager,
dispatch::{NotificationDispatcher, RequestDispatcher},
distro::Distribution,
features::{
@@ -24,14 +26,14 @@ use crate::{
ForwardSearchStatus,
},
req_queue::{IncomingData, ReqQueue},
- ClientCapabilitiesExt, DocumentLanguage, Environment, LineIndex, LineIndexExt, Options,
- Workspace, WorkspaceEvent,
+ ClientCapabilitiesExt, Document, DocumentData, DocumentLanguage, Environment, LineIndex,
+ LineIndexExt, Options, Workspace, WorkspaceEvent,
};
#[derive(Debug)]
enum InternalMessage {
SetDistro(Distribution),
- SetOptions(Options),
+ SetOptions(Arc<Options>),
}
#[derive(Clone)]
@@ -41,8 +43,8 @@ pub struct Server {
internal_rx: Receiver<InternalMessage>,
req_queue: Arc<Mutex<ReqQueue>>,
workspace: Workspace,
- static_debouncer: Arc<DiagnosticsDebouncer>,
- chktex_debouncer: Arc<DiagnosticsDebouncer>,
+ diagnostic_tx: debouncer::Sender<Workspace>,
+ diagnostic_manager: DiagnosticManager,
pool: Arc<Mutex<ThreadPool>>,
load_resolver: bool,
build_engine: Arc<BuildEngine>,
@@ -56,25 +58,17 @@ impl Server {
) -> Self {
let req_queue = Arc::default();
let workspace = Workspace::new(Environment::new(Arc::new(current_dir)));
- let diag_manager = Arc::new(Mutex::new(DiagnosticsManager::default()));
-
- let static_debouncer = Arc::new(create_static_debouncer(
- Arc::clone(&diag_manager),
- &connection,
- ));
-
- let chktex_debouncer = Arc::new(create_chktex_debouncer(diag_manager, &connection));
-
let (internal_tx, internal_rx) = crossbeam_channel::unbounded();
-
+ let diagnostic_manager = DiagnosticManager::default();
+ let diagnostic_tx = create_debouncer(connection.sender.clone(), diagnostic_manager.clone());
Self {
connection: Arc::new(connection),
internal_tx,
internal_rx,
req_queue,
workspace,
- static_debouncer,
- chktex_debouncer,
+ diagnostic_tx,
+ diagnostic_manager,
pool: Arc::new(Mutex::new(threadpool::Builder::new().build())),
load_resolver,
build_engine: Arc::default(),
@@ -173,7 +167,7 @@ impl Server {
self.spawn(move |server| {
server.register_config_capability();
server.register_file_watching();
- server.pull_config();
+ let _ = server.pull_config();
});
Ok(())
@@ -246,19 +240,17 @@ impl Server {
fn register_diagnostics_handler(&mut self) {
let (event_sender, event_receiver) = crossbeam_channel::unbounded();
- let diag_sender = self.static_debouncer.sender.clone();
+ let diagnostic_tx = self.diagnostic_tx.clone();
+ let diagnostic_manager = self.diagnostic_manager.clone();
std::thread::spawn(move || {
for event in event_receiver {
match event {
WorkspaceEvent::Changed(workspace, document) => {
- let message = DiagnosticsMessage::Analyze {
- workspace,
- document,
- };
-
- if diag_sender.send(message).is_err() {
- break;
- }
+ diagnostic_manager.push_syntax(&workspace, &document.uri);
+ let delay = workspace.environment.options.diagnostics_delay;
+ diagnostic_tx
+ .send(workspace, Duration::from_millis(delay))
+ .unwrap();
}
};
}
@@ -272,14 +264,14 @@ impl Server {
req_queue.incoming.register(id, IncomingData);
}
- fn pull_config(&self) {
+ fn pull_config(&self) -> Result<()> {
if !self
.workspace
.environment
.client_capabilities
.has_pull_configuration_support()
{
- return;
+ return Ok(());
}
let params = ConfigurationParams {
@@ -296,22 +288,38 @@ impl Server {
) {
Ok(mut json) => {
let value = json.pop().expect("invalid configuration request");
- let options = match serde_json::from_value(value) {
- Ok(new_options) => new_options,
- Err(why) => {
- warn!("Invalid configuration section \"texlab\": {}", why);
- Options::default()
- }
- };
-
+ let options = self.parse_options(value)?;
self.internal_tx
- .send(InternalMessage::SetOptions(options))
+ .send(InternalMessage::SetOptions(Arc::new(options)))
.unwrap();
}
Err(why) => {
error!("Retrieving configuration failed: {}", why);
}
};
+
+ Ok(())
+ }
+
+ fn parse_options(&self, value: serde_json::Value) -> Result<Options> {
+ let options = match serde_json::from_value(value) {
+ Ok(new_options) => new_options,
+ Err(why) => {
+ send_notification::<ShowMessage>(
+ &self.connection.sender,
+ ShowMessageParams {
+ message: format!(
+ "The texlab configuration is invalid; using the default settings instead.\nDetails: {why}"
+ ),
+ typ: MessageType::WARNING,
+ },
+ )?;
+
+ Options::default()
+ }
+ };
+
+ Ok(options)
}
fn cancel(&self, params: CancelParams) -> Result<()> {
@@ -352,18 +360,11 @@ impl Server {
.has_pull_configuration_support()
{
self.spawn(move |server| {
- server.pull_config();
+ let _ = server.pull_config();
});
} else {
- match serde_json::from_value(params.settings) {
- Ok(options) => {
- self.workspace.environment.options = Arc::new(options);
- }
- Err(why) => {
- error!("Invalid configuration: {}", why);
- }
- };
-
+ let options = self.parse_options(params.settings)?;
+ self.workspace.environment.options = Arc::new(options);
self.reparse_all()?;
}
@@ -382,12 +383,7 @@ impl Server {
self.workspace.viewport.insert(Arc::clone(&document.uri));
if self.workspace.environment.options.chktex.on_open_and_save {
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document,
- })?;
+ self.run_chktex(document);
}
Ok(())
@@ -421,12 +417,7 @@ impl Server {
);
if self.workspace.environment.options.chktex.on_edit {
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document: new_document,
- })?;
+ self.run_chktex(new_document);
};
}
None => match uri.to_file_path() {
@@ -440,7 +431,7 @@ impl Server {
Ok(())
}
- fn did_save(&self, params: DidSaveTextDocumentParams) -> Result<()> {
+ fn did_save(&mut self, params: DidSaveTextDocumentParams) -> Result<()> {
let uri = params.text_document.uri;
if let Some(request) = self
@@ -477,13 +468,9 @@ impl Server {
.filter(|_| self.workspace.environment.options.chktex.on_open_and_save)
.cloned()
{
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document,
- })?;
- };
+ self.run_chktex(document);
+ }
+
Ok(())
}
@@ -492,6 +479,20 @@ impl Server {
Ok(())
}
+ fn run_chktex(&mut self, document: Document) {
+ self.spawn(move |server| {
+ server
+ .diagnostic_manager
+ .push_chktex(&server.workspace, &document.uri);
+
+ let delay = server.workspace.environment.options.diagnostics_delay;
+ server
+ .diagnostic_tx
+ .send(server.workspace.clone(), Duration::from_millis(delay))
+ .unwrap();
+ });
+ }
+
fn feature_request<P>(&self, uri: Arc<Url>, params: P) -> FeatureRequest<P> {
FeatureRequest {
params,
@@ -841,7 +842,7 @@ impl Server {
self.reparse_all()?;
}
InternalMessage::SetOptions(options) => {
- self.workspace.environment.options = Arc::new(options);
+ self.workspace.environment.options = options;
self.reparse_all()?;
}
};
@@ -853,50 +854,40 @@ impl Server {
pub fn run(mut self) -> Result<()> {
self.initialize()?;
self.process_messages()?;
- drop(self.static_debouncer);
- drop(self.chktex_debouncer);
self.pool.lock().unwrap().join();
Ok(())
}
}
-fn create_static_debouncer(
- manager: Arc<Mutex<DiagnosticsManager>>,
- conn: &Connection,
-) -> DiagnosticsDebouncer {
- let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(move |workspace, document| {
- let mut manager = manager.lock().unwrap();
- manager.update_static(&workspace, Arc::clone(&document.uri));
- if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
- warn!("Failed to publish diagnostics: {}", why);
+fn create_debouncer(
+ lsp_sender: Sender<Message>,
+ diagnostic_manager: DiagnosticManager,
+) -> debouncer::Sender<Workspace> {
+ let (tx, rx) = debouncer::unbounded();
+ std::thread::spawn(move || {
+ while let Ok(workspace) = rx.recv() {
+ if let Err(why) = publish_diagnostics(&lsp_sender, &diagnostic_manager, &workspace) {
+ warn!("Failed to publish diagnostics: {}", why);
+ }
}
- })
-}
+ });
-fn create_chktex_debouncer(
- manager: Arc<Mutex<DiagnosticsManager>>,
- conn: &Connection,
-) -> DiagnosticsDebouncer {
- let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(move |workspace, document| {
- let mut manager = manager.lock().unwrap();
- manager.update_chktex(&workspace, &document.uri, &workspace.environment.options);
- if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
- warn!("Failed to publish diagnostics: {}", why);
- }
- })
+ tx
}
fn publish_diagnostics(
- sender: &Sender<lsp_server::Message>,
+ lsp_sender: &Sender<lsp_server::Message>,
+ diagnostic_manager: &DiagnosticManager,
workspace: &Workspace,
- diag_manager: &DiagnosticsManager,
) -> Result<()> {
for document in workspace.documents_by_uri.values() {
- let diagnostics = diag_manager.publish(&document.uri);
+ if matches!(document.data, DocumentData::BuildLog(_)) {
+ continue;
+ }
+
+ let diagnostics = diagnostic_manager.publish(workspace, &document.uri);
send_notification::<PublishDiagnostics>(
- sender,
+ lsp_sender,
PublishDiagnosticsParams {
uri: document.uri.as_ref().clone(),
version: None,
@@ -904,6 +895,7 @@ fn publish_diagnostics(
},
)?;
}
+
Ok(())
}
diff --git a/support/texlab/tests/integration/dev.rs b/support/texlab/tests/integration/dev.rs
deleted file mode 100644
index 7ba1e93d0c..0000000000
--- a/support/texlab/tests/integration/dev.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::process::{Command, Stdio};
-
-use anyhow::Result;
-
-#[test]
-fn rustfmt() -> Result<()> {
- let success = Command::new("cargo")
- .args(&["fmt", "--check"])
- .current_dir(env!("CARGO_MANIFEST_DIR"))
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .status()?
- .success();
-
- assert!(success);
- Ok(())
-}
diff --git a/support/texlab/tests/integration/lsp/client.rs b/support/texlab/tests/integration/lsp/client.rs
index dc6b66795c..004422813a 100644
--- a/support/texlab/tests/integration/lsp/client.rs
+++ b/support/texlab/tests/integration/lsp/client.rs
@@ -142,6 +142,13 @@ impl Client {
Ok(())
}
+ pub fn store_on_disk(&mut self, name: &str, text: &str) -> Result<()> {
+ let path = self.directory.path().join(name);
+ std::fs::create_dir_all(path.parent().unwrap())?;
+ std::fs::write(path, text)?;
+ Ok(())
+ }
+
pub fn shutdown(mut self) -> Result<ClientResult> {
self.request::<Shutdown>(())?;
self.notify::<Exit>(())?;
diff --git a/support/texlab/tests/integration/lsp/fixture.rs b/support/texlab/tests/integration/lsp/fixture.rs
index 17eb659923..83d63cce60 100644
--- a/support/texlab/tests/integration/lsp/fixture.rs
+++ b/support/texlab/tests/integration/lsp/fixture.rs
@@ -20,6 +20,8 @@ fn parse_line(line: &str) -> Line {
Line::File(name, "latex")
} else if let Some(name) = line.strip_prefix("%BIB ") {
Line::File(name, "bibtex")
+ } else if let Some(name) = line.strip_prefix("%LOG ") {
+ Line::File(name, "log")
} else if let Some(text) = line.strip_prefix("%SRC ") {
Line::Plain(text)
} else if let Some(text) = line.strip_prefix("%CUR ") {
diff --git a/support/texlab/tests/integration/lsp/text_document.rs b/support/texlab/tests/integration/lsp/text_document.rs
index 56ad1ee09f..283132d3c4 100644
--- a/support/texlab/tests/integration/lsp/text_document.rs
+++ b/support/texlab/tests/integration/lsp/text_document.rs
@@ -1,11 +1,13 @@
#[cfg(feature = "completion")]
mod completion;
mod definition;
+mod did_change_configuration;
mod document_highlight;
mod document_link;
mod document_symbol;
mod folding_range;
mod formatting;
mod hover;
+mod publish_diagnostics;
mod references;
mod rename;
diff --git a/support/texlab/tests/integration/lsp/text_document/did_change_configuration.rs b/support/texlab/tests/integration/lsp/text_document/did_change_configuration.rs
new file mode 100644
index 0000000000..c45dca094b
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/did_change_configuration.rs
@@ -0,0 +1,39 @@
+use anyhow::Result;
+use insta::assert_snapshot;
+use lsp_types::{
+ notification::{DidChangeConfiguration, Notification, ShowMessage},
+ ClientCapabilities, DidChangeConfigurationParams, ShowMessageParams,
+};
+
+use crate::lsp::client::Client;
+
+#[test]
+fn invalid_configuration() -> Result<()> {
+ let mut client = Client::spawn()?;
+ client.initialize(ClientCapabilities::default(), None)?;
+
+ client.notify::<DidChangeConfiguration>(DidChangeConfigurationParams {
+ settings: serde_json::json!({
+ "diagnostics": {
+ "allowedPatterns": ["\\"]
+ }
+ }),
+ })?;
+
+ let result = client.shutdown()?;
+ let message = result
+ .incoming
+ .notifications
+ .into_iter()
+ .filter_map(|notification| {
+ notification
+ .extract::<ShowMessageParams>(ShowMessage::METHOD)
+ .ok()
+ })
+ .find(|params| params.message.contains("configuration"))
+ .unwrap()
+ .message;
+
+ assert_snapshot!(message);
+ Ok(())
+}
diff --git a/support/texlab/tests/integration/lsp/text_document/publish_diagnostics.rs b/support/texlab/tests/integration/lsp/text_document/publish_diagnostics.rs
new file mode 100644
index 0000000000..7e406b5319
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/publish_diagnostics.rs
@@ -0,0 +1,264 @@
+use anyhow::Result;
+use insta::{assert_json_snapshot, internals::Redaction};
+use lsp_types::{
+ notification::{
+ DidChangeConfiguration, DidChangeWatchedFiles, Notification, PublishDiagnostics,
+ },
+ ClientCapabilities, Diagnostic, DidChangeConfigurationParams, DidChangeWatchedFilesParams,
+ FileChangeType, FileEvent, PublishDiagnosticsParams, Url,
+};
+use rustc_hash::FxHashMap;
+
+use crate::lsp::{client::Client, fixture};
+
+struct DiagnosticResult {
+ all_diagnostics: FxHashMap<Url, Vec<Diagnostic>>,
+ uri_redaction: Redaction,
+}
+
+fn find_diagnostics(fixture: &str, settings: serde_json::Value) -> Result<DiagnosticResult> {
+ let mut client = Client::spawn()?;
+ client.initialize(ClientCapabilities::default(), None)?;
+
+ let mut disk_files = Vec::new();
+ let fixture = fixture::parse(fixture);
+ for file in fixture.files {
+ if file.lang == "log" {
+ client.store_on_disk(file.name, &file.text)?;
+ disk_files.push(client.uri(file.name)?);
+ } else {
+ client.open(file.name, file.lang, file.text)?;
+ }
+ }
+
+ client.notify::<DidChangeWatchedFiles>(DidChangeWatchedFilesParams {
+ changes: disk_files
+ .into_iter()
+ .map(|uri| FileEvent::new(uri, FileChangeType::CHANGED))
+ .collect(),
+ })?;
+
+ client.notify::<DidChangeConfiguration>(DidChangeConfigurationParams { settings })?;
+
+ let result = client.shutdown()?;
+
+ let uri = Url::from_directory_path(result.directory.path()).unwrap();
+ let uri_redaction = insta::dynamic_redaction(move |content, _path| {
+ content.as_str().unwrap().replace(uri.as_str(), "[tmp]/")
+ });
+
+ let all_diagnostics = result
+ .incoming
+ .notifications
+ .into_iter()
+ .filter_map(|notification| {
+ notification
+ .extract::<PublishDiagnosticsParams>(PublishDiagnostics::METHOD)
+ .ok()
+ })
+ .map(|params| (params.uri, params.diagnostics))
+ .collect();
+
+ Ok(DiagnosticResult {
+ all_diagnostics,
+ uri_redaction,
+ })
+}
+
+macro_rules! assert_symbols {
+ ($result:expr) => {
+ let result = $result;
+ assert_json_snapshot!(result.all_diagnostics, {
+ ".$key" => result.uri_redaction
+ });
+ };
+}
+
+static BUILD_LOG_FIXTURE: &str = r#"
+%TEX main.tex
+%SRC \documentclass{article}
+%SRC
+%SRC \usepackage{amsmath}
+%SRC
+%SRC \begin{document}
+%SRC
+%SRC \foo{}
+%SRC aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+%SRC
+%SRC \end{document}
+
+%LOG main.log
+%SRC This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021/W32TeX) (preloaded format=pdflatex 2022.1.23) 16 JUN 2022 11:04
+%SRC entering extended mode
+%SRC restricted \write18 enabled.
+%SRC %&-line parsing enabled.
+%SRC **main.tex
+%SRC (./main.tex
+%SRC LaTeX2e <2020-10-01> patch level 4
+%SRC L3 programming layer <2021-02-18>
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/base/article.cls
+%SRC Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/base/size10.clo
+%SRC File: size10.clo 2020/04/10 v1.4m Standard LaTeX file (size option)
+%SRC )
+%SRC \c@part=\count179
+%SRC \c@section=\count180
+%SRC \c@subsection=\count181
+%SRC \c@subsubsection=\count182
+%SRC \c@paragraph=\count183
+%SRC \c@subparagraph=\count184
+%SRC \c@figure=\count185
+%SRC \c@table=\count186
+%SRC \abovecaptionskip=\skip47
+%SRC \belowcaptionskip=\skip48
+%SRC \bibindent=\dimen138
+%SRC )
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/amsmath/amsmath.sty
+%SRC Package: amsmath 2020/09/23 v2.17i AMS math features
+%SRC \@mathmargin=\skip49
+%SRC
+%SRC For additional information on amsmath, use the `?' option.
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/amsmath/amstext.sty
+%SRC Package: amstext 2000/06/29 v2.01 AMS text
+%SRC
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/amsmath/amsgen.sty
+%SRC File: amsgen.sty 1999/11/30 v2.0 generic functions
+%SRC \@emptytoks=\toks15
+%SRC \ex@=\dimen139
+%SRC ))
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/amsmath/amsbsy.sty
+%SRC Package: amsbsy 1999/11/29 v1.2d Bold Symbols
+%SRC \pmbraise@=\dimen140
+%SRC )
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/amsmath/amsopn.sty
+%SRC Package: amsopn 2016/03/08 v2.02 operator names
+%SRC )
+%SRC \inf@bad=\count187
+%SRC LaTeX Info: Redefining \frac on input line 234.
+%SRC \uproot@=\count188
+%SRC \leftroot@=\count189
+%SRC LaTeX Info: Redefining \overline on input line 399.
+%SRC \classnum@=\count190
+%SRC \DOTSCASE@=\count191
+%SRC LaTeX Info: Redefining \ldots on input line 496.
+%SRC LaTeX Info: Redefining \dots on input line 499.
+%SRC LaTeX Info: Redefining \cdots on input line 620.
+%SRC \Mathstrutbox@=\box47
+%SRC \strutbox@=\box48
+%SRC \big@size=\dimen141
+%SRC LaTeX Font Info: Redeclaring font encoding OML on input line 743.
+%SRC LaTeX Font Info: Redeclaring font encoding OMS on input line 744.
+%SRC \macc@depth=\count192
+%SRC \c@MaxMatrixCols=\count193
+%SRC \dotsspace@=\muskip16
+%SRC \c@parentequation=\count194
+%SRC \dspbrk@lvl=\count195
+%SRC \tag@help=\toks16
+%SRC \row@=\count196
+%SRC \column@=\count197
+%SRC \maxfields@=\count198
+%SRC \andhelp@=\toks17
+%SRC \eqnshift@=\dimen142
+%SRC \alignsep@=\dimen143
+%SRC \tagshift@=\dimen144
+%SRC \tagwidth@=\dimen145
+%SRC \totwidth@=\dimen146
+%SRC \lineht@=\dimen147
+%SRC \@envbody=\toks18
+%SRC \multlinegap=\skip50
+%SRC \multlinetaggap=\skip51
+%SRC \mathdisplay@stack=\toks19
+%SRC LaTeX Info: Redefining \[ on input line 2923.
+%SRC LaTeX Info: Redefining \] on input line 2924.
+%SRC )
+%SRC (c:/texlive/2021/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
+%SRC File: l3backend-pdftex.def 2021-03-18 L3 backend support: PDF output (pdfTeX)
+%SRC \l__color_backend_stack_int=\count199
+%SRC \l__pdf_internal_box=\box49
+%SRC )
+%SRC (./main.aux)
+%SRC \openout1 = `main.aux'.
+%SRC
+%SRC LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 5.
+%SRC LaTeX Font Info: ... okay on input line 5.
+%SRC
+%SRC ! Undefined control sequence.
+%SRC l.7 \foo
+%SRC {}
+%SRC The control sequence at the end of the top line
+%SRC of your error message was never \def'ed. If you have
+%SRC misspelled it (e.g., `\hobx'), type `I' and the correct
+%SRC spelling (e.g., `I\hbox'). Otherwise just continue,
+%SRC and I'll forget about whatever was undefined.
+%SRC
+%SRC
+%SRC Overfull \hbox (80.00125pt too wide) in paragraph at lines 8--9
+%SRC []\OT1/cmr/m/n/10 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+%SRC aaaaaaaaaaaaaaaaaaaaa
+%SRC []
+%SRC
+%SRC [1
+%SRC
+%SRC {c:/texlive/2021/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./main.aux) )
+%SRC Here is how much of TeX's memory you used:
+%SRC 1014 strings out of 478994
+%SRC 13996 string characters out of 5862207
+%SRC 300924 words of memory out of 5000000
+%SRC 18565 multiletter control sequences out of 15000+600000
+%SRC 403430 words of font info for 27 fonts, out of 8000000 for 9000
+%SRC 1141 hyphenation exceptions out of 8191
+%SRC 67i,4n,74p,200b,104s stack positions out of 5000i,500n,10000p,200000b,80000s
+%SRC <
+%SRC c:/texlive/2021/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
+%SRC Output written on main.pdf (1 page, 9741 bytes).
+%SRC PDF statistics:
+%SRC 12 PDF objects out of 1000 (max. 8388607)
+%SRC 7 compressed objects within 1 object stream
+%SRC 0 named destinations out of 1000 (max. 500000)
+%SRC 1 words of extra memory for PDF output out of 10000 (max. 10000000)"#;
+
+#[test]
+fn build_log_filter_none() -> Result<()> {
+ assert_symbols!(find_diagnostics(BUILD_LOG_FIXTURE, serde_json::json!({}))?);
+ Ok(())
+}
+
+#[test]
+fn build_log_filter_allowed() -> Result<()> {
+ assert_symbols!(find_diagnostics(
+ BUILD_LOG_FIXTURE,
+ serde_json::json!({
+ "diagnostics": {
+ "allowedPatterns": ["Overfull \\\\[hv]box"]
+ }
+ })
+ )?);
+
+ Ok(())
+}
+
+#[test]
+fn build_log_filter_ignored() -> Result<()> {
+ assert_symbols!(find_diagnostics(
+ BUILD_LOG_FIXTURE,
+ serde_json::json!({
+ "diagnostics": {
+ "ignoredPatterns": ["Overfull \\\\[hv]box"]
+ }
+ })
+ )?);
+
+ Ok(())
+}
diff --git a/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__did_change_configuration__invalid_configuration.snap b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__did_change_configuration__invalid_configuration.snap
new file mode 100644
index 0000000000..4f41cfbeb2
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__did_change_configuration__invalid_configuration.snap
@@ -0,0 +1,10 @@
+---
+source: tests/integration/lsp/text_document/did_change_configuration.rs
+assertion_line: 37
+expression: message
+---
+The texlab configuration is invalid; using the default settings instead.
+Details: regex parse error:
+ \
+ ^
+error: incomplete escape sequence, reached end of pattern prematurely
diff --git a/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_allowed.snap b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_allowed.snap
new file mode 100644
index 0000000000..9fc11d295b
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_allowed.snap
@@ -0,0 +1,24 @@
+---
+source: tests/integration/lsp/text_document/publish_diagnostics.rs
+assertion_line: 240
+expression: result.all_diagnostics
+---
+{
+ "[tmp]/main.tex": [
+ {
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 0
+ }
+ },
+ "severity": 2,
+ "source": "latex-build",
+ "message": "Overfull \\hbox (80.00125pt too wide) in paragraph at lines 8--9"
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_ignored.snap b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_ignored.snap
new file mode 100644
index 0000000000..28e0308a72
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_ignored.snap
@@ -0,0 +1,24 @@
+---
+source: tests/integration/lsp/text_document/publish_diagnostics.rs
+assertion_line: 254
+expression: result.all_diagnostics
+---
+{
+ "[tmp]/main.tex": [
+ {
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 0
+ }
+ },
+ "severity": 1,
+ "source": "latex-build",
+ "message": "Undefined control sequence."
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_none.snap b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_none.snap
new file mode 100644
index 0000000000..b16238381b
--- /dev/null
+++ b/support/texlab/tests/integration/lsp/text_document/snapshots/integration__lsp__text_document__publish_diagnostics__build_log_filter_none.snap
@@ -0,0 +1,39 @@
+---
+source: tests/integration/lsp/text_document/publish_diagnostics.rs
+assertion_line: 234
+expression: result.all_diagnostics
+---
+{
+ "[tmp]/main.tex": [
+ {
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 0
+ }
+ },
+ "severity": 1,
+ "source": "latex-build",
+ "message": "Undefined control sequence."
+ },
+ {
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 0
+ }
+ },
+ "severity": 2,
+ "source": "latex-build",
+ "message": "Overfull \\hbox (80.00125pt too wide) in paragraph at lines 8--9"
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/main.rs b/support/texlab/tests/integration/main.rs
index 508158af91..44e0d3635a 100644
--- a/support/texlab/tests/integration/main.rs
+++ b/support/texlab/tests/integration/main.rs
@@ -1,2 +1 @@
-mod dev;
mod lsp;