From 68c5442089b7c2e61ee14fc3efed490e291a244a Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 2 May 2024 03:02:55 +0000 Subject: CTAN sync 202405020302 --- support/texlab/CHANGELOG.md | 36 +++ support/texlab/Cargo.lock | 247 +++++++++++++------- support/texlab/Cargo.toml | 2 +- support/texlab/README.md | 10 +- support/texlab/crates/base-db/Cargo.toml | 6 +- support/texlab/crates/base-db/src/deps.rs | 2 +- support/texlab/crates/base-db/src/deps/discover.rs | 4 +- support/texlab/crates/base-db/src/deps/graph.rs | 8 +- support/texlab/crates/base-db/src/deps/root.rs | 40 ++-- support/texlab/crates/base-db/src/document.rs | 7 +- support/texlab/crates/base-db/src/semantics.rs | 9 + support/texlab/crates/base-db/src/semantics/tex.rs | 29 +-- support/texlab/crates/base-db/src/util.rs | 2 + support/texlab/crates/base-db/src/util/expand.rs | 31 +++ support/texlab/crates/base-db/src/workspace.rs | 20 +- support/texlab/crates/bibfmt/Cargo.toml | 2 +- support/texlab/crates/bibtex-utils/Cargo.toml | 6 +- support/texlab/crates/citeproc/Cargo.toml | 6 +- support/texlab/crates/commands/Cargo.toml | 10 +- support/texlab/crates/completion-data/Cargo.toml | 6 +- support/texlab/crates/completion/Cargo.toml | 4 +- .../crates/completion/src/providers/include.rs | 13 +- support/texlab/crates/completion/src/tests.rs | 46 ++++ support/texlab/crates/definition/Cargo.toml | 2 +- support/texlab/crates/definition/src/command.rs | 28 ++- support/texlab/crates/definition/src/tests.rs | 16 +- support/texlab/crates/diagnostics/Cargo.toml | 8 +- support/texlab/crates/diagnostics/src/citations.rs | 2 +- support/texlab/crates/diagnostics/src/manager.rs | 14 +- support/texlab/crates/distro/Cargo.toml | 2 +- support/texlab/crates/folding/Cargo.toml | 2 +- support/texlab/crates/highlights/Cargo.toml | 2 +- support/texlab/crates/hover/Cargo.toml | 2 +- support/texlab/crates/inlay-hints/Cargo.toml | 2 +- support/texlab/crates/ipc/Cargo.toml | 21 ++ support/texlab/crates/ipc/src/lib.rs | 54 +++++ support/texlab/crates/links/Cargo.toml | 2 +- support/texlab/crates/parser/Cargo.toml | 9 +- support/texlab/crates/parser/src/bibtex.rs | 4 +- support/texlab/crates/parser/src/config.rs | 9 + support/texlab/crates/parser/src/latex.rs | 22 +- .../crates/parser/src/latex/lexer/commands.rs | 31 ++- .../texlab/crates/parser/src/latex/lexer/types.rs | 3 +- support/texlab/crates/parser/src/latex/tests.rs | 251 +++++++++++++-------- support/texlab/crates/parser/src/latexmkrc.rs | 185 +++++++++++---- support/texlab/crates/references/Cargo.toml | 1 + support/texlab/crates/references/src/command.rs | 52 +++++ support/texlab/crates/references/src/lib.rs | 2 + support/texlab/crates/references/src/tests.rs | 31 +++ support/texlab/crates/symbols/Cargo.toml | 8 +- support/texlab/crates/syntax/Cargo.toml | 2 +- support/texlab/crates/syntax/src/latex/cst.rs | 35 ++- support/texlab/crates/syntax/src/latex/kind.rs | 3 +- support/texlab/crates/texlab/Cargo.toml | 13 +- support/texlab/crates/texlab/src/main.rs | 67 +++++- support/texlab/crates/texlab/src/server.rs | 102 +++++++-- support/texlab/crates/texlab/src/server/options.rs | 136 +---------- support/texlab/crates/texlab/src/util.rs | 2 +- .../texlab/crates/texlab/src/util/client_flags.rs | 3 + .../texlab/crates/texlab/src/util/from_proto.rs | 150 +++++++++++- support/texlab/texlab.1 | 16 +- support/texlab/texlab.pdf | Bin 26268 -> 26307 bytes 62 files changed, 1328 insertions(+), 512 deletions(-) create mode 100644 support/texlab/crates/base-db/src/util/expand.rs create mode 100644 support/texlab/crates/ipc/Cargo.toml create mode 100644 support/texlab/crates/ipc/src/lib.rs create mode 100644 support/texlab/crates/references/src/command.rs (limited to 'support') diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md index 4b3705b115..0a3b5ed683 100644 --- a/support/texlab/CHANGELOG.md +++ b/support/texlab/CHANGELOG.md @@ -5,6 +5,42 @@ 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.16.0] - 2024-05-01 + +### Added + +- Add `texlab.experimental.labelDefinitionCommands` setting to customize the list of `\label`-like commands + ([#1093](https://github.com/latex-lsp/texlab/issues/1093)) +- Extend list of commands treated as command definitions and environment definitions ([#1087](https://github.com/latex-lsp/texlab/pull/1087)) +- Allow the server to trigger the forward search in an editor-agnostic way: + Running `texlab inverse-search --input --line ` will open the specified document in the editor if the client supports it + ([#1091](https://github.com/latex-lsp/texlab/issues/1091)) + +### Fixed + +- Fix detection of log dir when using `latexmkrc` (use `$aux_dir` instead of `$out_dir`) ([#1079](https://github.com/latex-lsp/texlab/pull/1079)) +- Fix extracting `$aux_dir` and `$out_dir` from `latexmkrc` ([#1079](https://github.com/latex-lsp/texlab/pull/1079)) +- Add support for extracting `$aux_dir` and `$out_dir` from `latexmk >= v4.85` (([#1079](https://github.com/latex-lsp/texlab/pull/1079))) +- Improve log file change detection under Windows +- Cleanup diagnostics of deleted files +- Let `\declaretheorem` accept a list of environment names instead of just a single name ([#1075](https://github.com/latex-lsp/texlab/issues/1075)) +- Use configured directories if not set explicitly by `latexmkrc` file ([#1095](https://github.com/latex-lsp/texlab/issues/1095)) + +## [5.15.0] - 2024-04-21 + +### Added + +- Replace tilde (`~`), environment variables, `${userHome}`, `${workspaceFolder}` in options +- Replace tidle (`~`) and environment variables in `\include`-like commands +- Add "Go To Definition" support for user-defined commands with `\def` and `\let` ([#1081](https://github.com/latex-lsp/texlab/issues/1081)) +- Add "Find all References" for commands ([#1082](https://github.com/latex-lsp/texlab/issues/1082)) +- Support more macros for defining commands ([#1083](https://github.com/latex-lsp/texlab/issues/1083)) + +### Fixed + +- Avoid panicking when malformed URIs are sent to the server ([#1078](https://github.com/latex-lsp/texlab/issues/1078)) +- Don't report errors for citations with macro arguments ([#1080](https://github.com/latex-lsp/texlab/issues/1080)) + ## [5.14.1] - 2024-03-27 ### Fixed diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock index 5f84112903..80599217fd 100644 --- a/support/texlab/Cargo.lock +++ b/support/texlab/Cargo.lock @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" @@ -90,7 +90,7 @@ dependencies = [ "bibtex-utils", "dirs", "distro", - "itertools 0.12.0", + "itertools 0.12.1", "line-index", "log", "notify", @@ -100,7 +100,9 @@ dependencies = [ "regex", "rowan", "rustc-hash", + "shellexpand", "syntax", + "titlecase", "url", ] @@ -128,7 +130,7 @@ dependencies = [ "chrono", "expect-test", "human_name", - "itertools 0.12.0", + "itertools 0.12.1", "parser", "rowan", "rustc-hash", @@ -150,9 +152,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "regex-automata", @@ -171,6 +173,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -221,7 +232,7 @@ dependencies = [ "bibtex-utils", "expect-test", "isocountry", - "itertools 0.12.0", + "itertools 0.12.1", "parser", "rowan", "rustc-hash", @@ -233,9 +244,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -255,9 +266,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", @@ -287,7 +298,7 @@ dependencies = [ "crossbeam-channel", "distro", "expect-test", - "itertools 0.12.0", + "itertools 0.12.1", "libc", "log", "rowan", @@ -298,6 +309,20 @@ dependencies = [ "url", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "serde", + "static_assertions", +] + [[package]] name = "completion" version = "0.0.0" @@ -322,7 +347,7 @@ name = "completion-data" version = "0.0.0" dependencies = [ "flate2", - "itertools 0.12.0", + "itertools 0.12.1", "once_cell", "rustc-hash", "serde", @@ -424,7 +449,7 @@ name = "definition" version = "0.0.0" dependencies = [ "base-db", - "itertools 0.12.0", + "itertools 0.12.1", "rowan", "rustc-hash", "syntax", @@ -439,7 +464,7 @@ dependencies = [ "encoding_rs", "encoding_rs_io", "expect-test", - "itertools 0.12.0", + "itertools 0.12.1", "line-index", "log", "multimap", @@ -495,9 +520,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -523,9 +548,9 @@ dependencies = [ [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -569,9 +594,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" dependencies = [ "crc32fast", "miniz_oxide", @@ -682,16 +707,16 @@ dependencies = [ [[package]] name = "human_name" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b68533610561604eed4d26b480b5fecfba40514672f961d6669825be847fda" +checksum = "7d40d3a11bc31949eb1d5070deb0cbb481bbd17d9423ecd3afdb3db5d81499b9" dependencies = [ + "compact_str", "crossbeam-utils", "phf", "phf_codegen", "serde", "serde_json", - "smallstr", "smallvec", "unicode-case-mapping", "unicode-normalization", @@ -740,6 +765,19 @@ dependencies = [ "libc", ] +[[package]] +name = "ipc" +version = "0.0.0" +dependencies = [ + "crossbeam-channel", + "expect-test", + "log", + "parser", + "serde", + "serde_json", + "uds_windows", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -772,9 +810,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -785,12 +823,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "joinery" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" - [[package]] name = "js-sys" version = "0.3.64" @@ -873,32 +905,33 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "logos" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" +checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" dependencies = [ "beef", "fnv", + "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.6.29", + "regex-syntax", "syn", ] [[package]] name = "logos-derive" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" +checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" dependencies = [ "logos-codegen", ] @@ -917,9 +950,9 @@ dependencies = [ [[package]] name = "lsp-types" -version = "0.95.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158c1911354ef73e8fe42da6b10c0484cb65c7f1007f28022e847706c1ab6984" +checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365" dependencies = [ "bitflags 1.3.2", "serde", @@ -943,6 +976,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -973,6 +1012,16 @@ dependencies = [ "serde", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "notify" version = "6.1.1" @@ -1071,15 +1120,24 @@ name = "parser" version = "0.0.0" dependencies = [ "expect-test", + "log", "logos", "once_cell", + "pathdiff", "regex", "rowan", "rustc-hash", "syntax", "tempfile", + "versions", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1187,9 +1245,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rayon" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1240,39 +1298,34 @@ version = "0.0.0" dependencies = [ "base-db", "rowan", + "rustc-hash", "syntax", "test-utils", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -1322,6 +1375,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + [[package]] name = "ryu" version = "1.0.15" @@ -1345,18 +1404,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -1365,9 +1424,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -1396,19 +1455,19 @@ dependencies = [ ] [[package]] -name = "siphasher" -version = "0.3.11" +name = "shellexpand" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs", +] [[package]] -name = "smallstr" -version = "0.3.0" +name = "siphasher" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b1aefdf380735ff8ded0b15f31aab05daf1f70216c01c02a12926badd1df9d" -dependencies = [ - "smallvec", -] +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" @@ -1416,6 +1475,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.0" @@ -1429,7 +1494,7 @@ dependencies = [ "base-db", "distro", "expect-test", - "itertools 0.12.0", + "itertools 0.12.1", "line-index", "regex", "rowan", @@ -1454,7 +1519,7 @@ dependencies = [ name = "syntax" version = "0.0.0" dependencies = [ - "itertools 0.12.0", + "itertools 0.12.1", "rowan", ] @@ -1483,7 +1548,7 @@ dependencies = [ [[package]] name = "texlab" -version = "5.14.1" +version = "5.16.0" dependencies = [ "anyhow", "base-db", @@ -1503,6 +1568,7 @@ dependencies = [ "highlights", "hover", "inlay-hints", + "ipc", "line-index", "links", "log", @@ -1535,18 +1601,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -1599,15 +1665,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "titlecase" -version = "2.2.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38397a8cdb017cfeb48bf6c154d6de975ac69ffeed35980fde199d2ee0842042" +checksum = "bb3223c7b6ee54ec8521af7b060ad616cb90a66d0b94d05af340de30cf02c514" dependencies = [ - "joinery", - "lazy_static", "regex", ] +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1665,6 +1740,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "versions" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a8931f8d167b6448076020e70b9de46dcf5ea1731212481a092d0071c4ac5b" +dependencies = [ + "itertools 0.12.1", + "nom", +] + [[package]] name = "walkdir" version = "2.4.0" diff --git a/support/texlab/Cargo.toml b/support/texlab/Cargo.toml index eeda5ec243..3894cb649f 100644 --- a/support/texlab/Cargo.toml +++ b/support/texlab/Cargo.toml @@ -9,7 +9,7 @@ authors = [ "Patrick Förster ", ] edition = "2021" -rust-version = "1.74" +rust-version = "1.75" [profile.release] lto = "thin" diff --git a/support/texlab/README.md b/support/texlab/README.md index 8e90b7064b..ae63109f10 100644 --- a/support/texlab/README.md +++ b/support/texlab/README.md @@ -1,7 +1,5 @@ [![CI](https://github.com/latex-lsp/texlab/workflows/CI/badge.svg)](https://github.com/latex-lsp/texlab/actions) -[![Coverage](https://codecov.io/gh/latex-lsp/texlab/branch/master/graph/badge.svg)](https://codecov.io/gh/latex-lsp/texlab) [![Wiki](https://img.shields.io/badge/docs-Wiki-success)](https://github.com/latex-lsp/texlab/wiki) - [![GitHub release](https://img.shields.io/github/release/latex-lsp/texlab?label=github)](https://github.com/latex-lsp/texlab/releases) [![CTAN](https://img.shields.io/ctan/v/texlab)](https://ctan.org/pkg/texlab) @@ -81,3 +79,11 @@ in the project folder. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. + +## Maintainers + +- Patrick Förster (patrick.foerster@outlook.de) + +## License + +[GPL-3.0](https://choosealicense.com/licenses/gpl-3.0/) diff --git a/support/texlab/crates/base-db/Cargo.toml b/support/texlab/crates/base-db/Cargo.toml index 010c83d0e4..ca477c496c 100644 --- a/support/texlab/crates/base-db/Cargo.toml +++ b/support/texlab/crates/base-db/Cargo.toml @@ -10,17 +10,19 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } dirs = "5.0.1" distro = { path = "../distro" } -itertools = "0.12.0" +itertools = "0.12.1" line-index = { path = "../line-index" } log = "0.4.21" notify = "6.0.1" once_cell = "1.19.0" parser = { path = "../parser" } percent-encoding = "2.3.0" -regex = "1.10.2" +regex = "1.10.4" rowan = "0.15.15" rustc-hash = "1.1.0" +shellexpand = "3.1.0" syntax = { path = "../syntax" } +titlecase = "3.0.0" url = "2.5.0" [lib] diff --git a/support/texlab/crates/base-db/src/deps.rs b/support/texlab/crates/base-db/src/deps.rs index 06689a0df2..f501d04935 100644 --- a/support/texlab/crates/base-db/src/deps.rs +++ b/support/texlab/crates/base-db/src/deps.rs @@ -5,7 +5,7 @@ mod root; pub use self::{ discover::{discover, watch}, - graph::{DirectLinkData, Edge, EdgeData, Graph}, + graph::{DirectLinkData, Edge, EdgeData, Graph, HOME_DIR}, project::{parents, Project}, root::ProjectRoot, }; diff --git a/support/texlab/crates/base-db/src/deps/discover.rs b/support/texlab/crates/base-db/src/deps/discover.rs index 06905905a5..cbcab4d8fb 100644 --- a/support/texlab/crates/base-db/src/deps/discover.rs +++ b/support/texlab/crates/base-db/src/deps/discover.rs @@ -84,7 +84,7 @@ fn discover_parents(workspace: &mut Workspace, checked_paths: &mut FxHashSet> = Lazy::new(dirs::home_dir); +pub static HOME_DIR: Lazy> = Lazy::new(dirs::home_dir); #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct Edge { @@ -158,7 +158,9 @@ impl Graph { for target_uri in file_names .iter() - .flat_map(|file_name| start.root.src_dir.join(file_name)) + .flat_map(|file_name| { + util::expand_relative_path(&file_name, &start.root.src_dir, workspace.folders()) + }) .chain(distro_files) { match workspace.lookup(&target_uri) { diff --git a/support/texlab/crates/base-db/src/deps/root.rs b/support/texlab/crates/base-db/src/deps/root.rs index 442d6f9e35..a8cec6604a 100644 --- a/support/texlab/crates/base-db/src/deps/root.rs +++ b/support/texlab/crates/base-db/src/deps/root.rs @@ -1,6 +1,6 @@ use url::Url; -use crate::{DocumentData, Workspace}; +use crate::{util, DocumentData, Workspace}; use super::graph::HOME_DIR; @@ -74,6 +74,7 @@ impl ProjectRoot { } pub fn from_latexmkrc(workspace: &Workspace, dir: &Url) -> Option { + let config = workspace.config(); let rcfile = workspace .iter() .filter(|document| document.dir == *dir) @@ -81,20 +82,30 @@ impl ProjectRoot { let compile_dir = dir.clone(); let src_dir = dir.clone(); - let aux_dir = rcfile + + let aux_dir_rc = rcfile .aux_dir .as_ref() - .and_then(|path| append_dir(dir, path).ok()) - .unwrap_or_else(|| dir.clone()); + .and_then(|path| append_dir(dir, path, workspace).ok()); - let out_dir = rcfile + let out_dir_rc = rcfile .out_dir .as_ref() - .and_then(|path| append_dir(dir, path).ok()) + .and_then(|path| append_dir(dir, path, workspace).ok()); + + let aux_dir = aux_dir_rc + .clone() + .or_else(|| append_dir(dir, &config.build.aux_dir, workspace).ok()) + .unwrap_or_else(|| dir.clone()); + + let log_dir = aux_dir_rc + .or_else(|| append_dir(dir, &config.build.log_dir, workspace).ok()) + .unwrap_or_else(|| dir.clone()); + + let pdf_dir = out_dir_rc + .or_else(|| append_dir(dir, &config.build.pdf_dir, workspace).ok()) .unwrap_or_else(|| dir.clone()); - let log_dir = out_dir.clone(); - let pdf_dir = out_dir; let additional_files = vec![]; Some(Self { @@ -124,9 +135,12 @@ impl ProjectRoot { let compile_dir = dir.clone(); let src_dir = dir.clone(); let config = workspace.config(); - let aux_dir = append_dir(dir, &config.build.aux_dir).unwrap_or_else(|_| dir.clone()); - let log_dir = append_dir(dir, &config.build.log_dir).unwrap_or_else(|_| dir.clone()); - let pdf_dir = append_dir(dir, &config.build.pdf_dir).unwrap_or_else(|_| dir.clone()); + let aux_dir = + append_dir(dir, &config.build.aux_dir, workspace).unwrap_or_else(|_| dir.clone()); + let log_dir = + append_dir(dir, &config.build.log_dir, workspace).unwrap_or_else(|_| dir.clone()); + let pdf_dir = + append_dir(dir, &config.build.pdf_dir, workspace).unwrap_or_else(|_| dir.clone()); let additional_files = vec![]; Self { @@ -140,13 +154,13 @@ impl ProjectRoot { } } -fn append_dir(dir: &Url, path: &str) -> Result { +fn append_dir(dir: &Url, path: &str, workspace: &Workspace) -> Result { let mut path = String::from(path); if !path.ends_with('/') { path.push('/'); } - dir.join(&path) + util::expand_relative_path(&path, dir, workspace.folders()) } impl std::fmt::Debug for ProjectRoot { diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs index 25607a1bf5..7a388129fd 100644 --- a/support/texlab/crates/base-db/src/document.rs +++ b/support/texlab/crates/base-db/src/document.rs @@ -77,7 +77,12 @@ impl Document { } Language::Root => DocumentData::Root, Language::Latexmkrc => { - let data = parser::parse_latexmkrc(&text).unwrap_or_default(); + let data = path + .as_deref() + .and_then(|path| path.parent()) + .and_then(|dir| parser::parse_latexmkrc(&text, dir).ok()) + .unwrap_or_default(); + DocumentData::Latexmkrc(data) } Language::Tectonic => DocumentData::Tectonic, diff --git a/support/texlab/crates/base-db/src/semantics.rs b/support/texlab/crates/base-db/src/semantics.rs index f83d758cf7..a2f0de291f 100644 --- a/support/texlab/crates/base-db/src/semantics.rs +++ b/support/texlab/crates/base-db/src/semantics.rs @@ -1,3 +1,5 @@ +use rowan::{TextLen, TextRange}; + pub mod auxiliary; pub mod bib; pub mod tex; @@ -19,6 +21,13 @@ impl Span { range: rowan::TextRange::empty(offset), } } + + pub fn command(token: &rowan::SyntaxToken) -> Self { + let range = token.text_range(); + let range = TextRange::new(range.start() + "\\".text_len(), range.end()); + let text = String::from(&token.text()[1..]); + Self::new(text, range) + } } impl std::fmt::Debug for Span { diff --git a/support/texlab/crates/base-db/src/semantics/tex.rs b/support/texlab/crates/base-db/src/semantics/tex.rs index 659ec8c166..1ad8969a80 100644 --- a/support/texlab/crates/base-db/src/semantics/tex.rs +++ b/support/texlab/crates/base-db/src/semantics/tex.rs @@ -1,6 +1,7 @@ -use rowan::{ast::AstNode, TextLen, TextRange}; +use rowan::{ast::AstNode, TextRange}; use rustc_hash::FxHashSet; use syntax::latex::{self, HasBrack, HasCurly}; +use titlecase::titlecase; use super::Span; @@ -26,10 +27,7 @@ impl Semantics { } latex::SyntaxElement::Token(token) => { if token.kind() == latex::COMMAND_NAME { - let range = token.text_range(); - let range = TextRange::new(range.start() + "\\".text_len(), range.end()); - let text = String::from(&token.text()[1..]); - self.commands.push(Span { range, text }); + self.commands.push(Span::command(&token)); } } }; @@ -257,18 +255,15 @@ impl Semantics { } fn process_theorem_definition(&mut self, theorem_def: latex::TheoremDefinition) { - let Some(name) = theorem_def.name().and_then(|name| name.key()) else { - return; - }; - - let Some(heading) = theorem_def.heading() else { - return; - }; - - self.theorem_definitions.push(TheoremDefinition { - name: Span::from(&name), - heading, - }); + for name in theorem_def.names() { + let name = Span::from(&name); + let heading = theorem_def + .heading() + .unwrap_or_else(|| titlecase(&name.text)); + + self.theorem_definitions + .push(TheoremDefinition { name, heading }); + } } fn process_graphics_path(&mut self, graphics_path: latex::GraphicsPath) { diff --git a/support/texlab/crates/base-db/src/util.rs b/support/texlab/crates/base-db/src/util.rs index 068a0c9b2a..789eee9c85 100644 --- a/support/texlab/crates/base-db/src/util.rs +++ b/support/texlab/crates/base-db/src/util.rs @@ -1,8 +1,10 @@ +mod expand; mod label; pub mod queries; mod regex_filter; pub use self::{ + expand::expand_relative_path, label::{render_label, FloatKind, RenderedLabel, RenderedObject}, regex_filter::filter_regex_patterns, }; diff --git a/support/texlab/crates/base-db/src/util/expand.rs b/support/texlab/crates/base-db/src/util/expand.rs new file mode 100644 index 0000000000..a315737115 --- /dev/null +++ b/support/texlab/crates/base-db/src/util/expand.rs @@ -0,0 +1,31 @@ +use std::{borrow::Cow, path::PathBuf}; + +use url::Url; + +use crate::deps::HOME_DIR; + +pub fn expand_relative_path( + path: &str, + current_dir: &Url, + workspace_folders: &[PathBuf], +) -> Result { + let home_dir = HOME_DIR.as_ref().and_then(|dir| dir.to_str()); + + let workspace_folder = workspace_folders.iter().find_map(|folder| { + let current_dir = current_dir.to_file_path().ok()?; + if current_dir.starts_with(folder) { + Some(folder.to_str()?) + } else { + None + } + }); + + let expand_var = |variable: &str| match variable { + "userHome" => home_dir.map(Cow::Borrowed), + "workspaceFolder" => Some(Cow::Borrowed(workspace_folder.unwrap_or("."))), + _ => std::env::var(variable).ok().map(Cow::Owned), + }; + + let path = shellexpand::full_with_context_no_errors(&path, || home_dir, expand_var); + current_dir.join(&path) +} diff --git a/support/texlab/crates/base-db/src/workspace.rs b/support/texlab/crates/base-db/src/workspace.rs index 1afd4712e9..75674dcf8d 100644 --- a/support/texlab/crates/base-db/src/workspace.rs +++ b/support/texlab/crates/base-db/src/workspace.rs @@ -29,11 +29,22 @@ impl Workspace { self.documents.get(key) } - pub fn lookup_path(&self, path: &Path) -> Option<&Document> { + pub fn lookup_file(&self, path: &Path) -> Option<&Document> { self.iter() .find(|document| document.path.as_deref() == Some(path)) } + pub fn lookup_file_or_dir<'a>( + &'a self, + file_or_dir: &'a Path, + ) -> impl Iterator + '_ { + self.iter().filter(move |doc| { + doc.path + .as_deref() + .map_or(false, |p| p.starts_with(file_or_dir)) + }) + } + pub fn iter(&self) -> impl Iterator + '_ { self.documents.iter() } @@ -50,6 +61,10 @@ impl Workspace { &self.graphs } + pub fn folders(&self) -> &[PathBuf] { + &self.folders + } + pub fn open( &mut self, uri: Url, @@ -90,7 +105,7 @@ impl Workspace { Owner::Server }; - if let Some(document) = self.lookup_path(path) { + if let Some(document) = self.lookup_file(path) { if document.text == text { return Ok(()); } @@ -173,6 +188,7 @@ impl Workspace { } pub fn remove(&mut self, uri: &Url) { + log::info!("Removing moved or deleted document: {uri}"); self.documents.remove(uri); } diff --git a/support/texlab/crates/bibfmt/Cargo.toml b/support/texlab/crates/bibfmt/Cargo.toml index 686e871d58..a712908d7d 100644 --- a/support/texlab/crates/bibfmt/Cargo.toml +++ b/support/texlab/crates/bibfmt/Cargo.toml @@ -15,5 +15,5 @@ line-index = { path = "../line-index" } doctest = false [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" parser = { path = "../parser" } diff --git a/support/texlab/crates/bibtex-utils/Cargo.toml b/support/texlab/crates/bibtex-utils/Cargo.toml index f0158c18a6..97e13517f5 100644 --- a/support/texlab/crates/bibtex-utils/Cargo.toml +++ b/support/texlab/crates/bibtex-utils/Cargo.toml @@ -8,8 +8,8 @@ rust-version.workspace = true [dependencies] chrono = { version = "0.4.26", default-features = false, features = ["std"] } -human_name = "2.0.2" -itertools = "0.12.0" +human_name = "2.0.3" +itertools = "0.12.1" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } @@ -19,5 +19,5 @@ unicode-normalization = "0.1.23" doctest = false [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" parser = { path = "../parser" } diff --git a/support/texlab/crates/citeproc/Cargo.toml b/support/texlab/crates/citeproc/Cargo.toml index bfc24fc83d..9e187f88e6 100644 --- a/support/texlab/crates/citeproc/Cargo.toml +++ b/support/texlab/crates/citeproc/Cargo.toml @@ -10,11 +10,11 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } base-db = { path = "../base-db" } isocountry = "0.3.2" -itertools = "0.12.0" +itertools = "0.12.1" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } -titlecase = "2.2.1" +titlecase = "3.0.0" unicode-normalization = "0.1.23" url = "2.5.0" @@ -22,5 +22,5 @@ url = "2.5.0" doctest = false [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" parser = { path = "../parser" } diff --git a/support/texlab/crates/commands/Cargo.toml b/support/texlab/crates/commands/Cargo.toml index bebcde27af..c726e5f0d5 100644 --- a/support/texlab/crates/commands/Cargo.toml +++ b/support/texlab/crates/commands/Cargo.toml @@ -7,22 +7,22 @@ edition.workspace = true rust-version.workspace = true [dependencies] -anyhow = "1.0.72" +anyhow = "1.0.82" base-db = { path = "../base-db" } -bstr = "1.9.0" +bstr = "1.9.1" crossbeam-channel = "0.5.12" -itertools = "0.12.0" +itertools = "0.12.1" libc = "0.2.153" log = "0.4.21" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } -thiserror = "1.0.58" +thiserror = "1.0.59" url = "2.5.0" [dev-dependencies] distro = { path = "../distro" } -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/completion-data/Cargo.toml b/support/texlab/crates/completion-data/Cargo.toml index 37ecf1b3cf..59b707e474 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.28" -itertools = "0.12.0" +flate2 = "1.0.29" +itertools = "0.12.1" once_cell = "1.19.0" rustc-hash = "1.1.0" serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.114" +serde_json = "1.0.115" [lib] doctest = false diff --git a/support/texlab/crates/completion/Cargo.toml b/support/texlab/crates/completion/Cargo.toml index fbdd2d7201..e28e04dfc7 100644 --- a/support/texlab/crates/completion/Cargo.toml +++ b/support/texlab/crates/completion/Cargo.toml @@ -11,7 +11,7 @@ base-db = { path = "../base-db" } completion-data = { path = "../completion-data" } fuzzy-matcher = { version = "0.3.7", features = ["compact"] } line-index = { path = "../line-index" } -rayon = "1.8.1" +rayon = "1.10.0" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } @@ -20,7 +20,7 @@ url = "2.5.0" [dev-dependencies] criterion = "0.5.1" distro = { path = "../distro" } -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/completion/src/providers/include.rs b/support/texlab/crates/completion/src/providers/include.rs index 6246624733..60c432d097 100644 --- a/support/texlab/crates/completion/src/providers/include.rs +++ b/support/texlab/crates/completion/src/providers/include.rs @@ -1,11 +1,11 @@ use std::{ - fs, + fs::{self}, path::{Path, PathBuf}, }; use base_db::{ deps::{self, ProjectRoot}, - DocumentData, FeatureParams, + util, DocumentData, FeatureParams, }; use rowan::{ast::AstNode, TextLen, TextRange}; use syntax::latex; @@ -121,10 +121,8 @@ fn current_dir( .map_or(params.document, Clone::clone); let root = ProjectRoot::walk_and_find(workspace, &parent.dir); - let path = root.src_dir.to_file_path().ok()?; - - let mut path = PathBuf::from(path.to_str()?.replace('\\', "/")); + let mut path = PathBuf::new(); if let Some(graphics_path) = graphics_path { path.push(graphics_path); } @@ -136,7 +134,10 @@ fn current_dir( } } - Some(path) + let current_dir = + util::expand_relative_path(path.to_str()?, &root.src_dir, workspace.folders()).ok()?; + + current_dir.to_file_path().ok() } fn is_included(file: &Path, allowed_extensions: &[&str]) -> bool { diff --git a/support/texlab/crates/completion/src/tests.rs b/support/texlab/crates/completion/src/tests.rs index f41c9f5e6b..c6bed6e08e 100644 --- a/support/texlab/crates/completion/src/tests.rs +++ b/support/texlab/crates/completion/src/tests.rs @@ -1676,6 +1676,52 @@ fn theorem_end() { ); } +#[test] +fn theorem_begin_multiple() { + check( + r#" +%! main.tex +\declaretheorem[sibling=table, style=thmbox]{def1, def2, def3, def4, def5} +\begin{def + | + ^^^"#, + expect![[r#" + [ + Environment( + EnvironmentData { + name: "def1", + package: "", + }, + ), + Environment( + EnvironmentData { + name: "def2", + package: "", + }, + ), + Environment( + EnvironmentData { + name: "def3", + package: "", + }, + ), + Environment( + EnvironmentData { + name: "def4", + package: "", + }, + ), + Environment( + EnvironmentData { + name: "def5", + package: "", + }, + ), + ] + "#]], + ); +} + #[test] fn tikz_library_open_brace() { check( diff --git a/support/texlab/crates/definition/Cargo.toml b/support/texlab/crates/definition/Cargo.toml index d8c4aa3a49..63ba15c308 100644 --- a/support/texlab/crates/definition/Cargo.toml +++ b/support/texlab/crates/definition/Cargo.toml @@ -13,7 +13,7 @@ rustc-hash = "1.1.0" syntax = { path = "../syntax" } [dev-dependencies] -itertools = "0.12.0" +itertools = "0.12.1" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/definition/src/command.rs b/support/texlab/crates/definition/src/command.rs index d618ca9ddf..3681cdbaa0 100644 --- a/support/texlab/crates/definition/src/command.rs +++ b/support/texlab/crates/definition/src/command.rs @@ -1,5 +1,5 @@ use base_db::DocumentData; -use rowan::ast::AstNode; +use rowan::{ast::AstNode, TextRange}; use syntax::latex; use crate::DefinitionContext; @@ -24,18 +24,16 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> { let results = data .root_node() .descendants() - .filter_map(latex::CommandDefinition::cast) - .filter(|def| { - def.name() - .and_then(|name| name.command()) - .map_or(false, |node| node.text() == name.text()) + .filter_map(|node| { + process_old_definition(node.clone()).or_else(|| process_new_definition(node)) }) - .filter_map(|def| { + .filter(|(_, command)| command.text() == name.text()) + .filter_map(|(target_range, command)| { Some(DefinitionResult { origin_selection_range, target: document, - target_range: latex::small_range(&def), - target_selection_range: def.name()?.command()?.text_range(), + target_range, + target_selection_range: command.text_range(), }) }); @@ -44,3 +42,15 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> { Some(()) } + +fn process_old_definition(node: latex::SyntaxNode) -> Option<(TextRange, latex::SyntaxToken)> { + let node = latex::OldCommandDefinition::cast(node)?; + let name = node.name()?; + Some((latex::small_range(&node), name)) +} + +fn process_new_definition(node: latex::SyntaxNode) -> Option<(TextRange, latex::SyntaxToken)> { + let node = latex::NewCommandDefinition::cast(node)?; + let name = node.name()?.command()?; + Some((latex::small_range(&node), name)) +} diff --git a/support/texlab/crates/definition/src/tests.rs b/support/texlab/crates/definition/src/tests.rs index 54b0e38f30..26f7c0dc3a 100644 --- a/support/texlab/crates/definition/src/tests.rs +++ b/support/texlab/crates/definition/src/tests.rs @@ -36,7 +36,21 @@ fn check(input: &str) { } #[test] -fn test_command_definition() { +fn test_old_command_definition() { + check( + r#" +%! main.tex +\def\foo{foo} + ^^^^ +^^^^^^^^ +\foo + | +^^^^"#, + ) +} + +#[test] +fn test_new_command_definition() { check( r#" %! main.tex diff --git a/support/texlab/crates/diagnostics/Cargo.toml b/support/texlab/crates/diagnostics/Cargo.toml index 5a2a7a7e28..1cb09fdec9 100644 --- a/support/texlab/crates/diagnostics/Cargo.toml +++ b/support/texlab/crates/diagnostics/Cargo.toml @@ -8,21 +8,21 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -encoding_rs = "0.8.33" +encoding_rs = "0.8.34" encoding_rs_io = "0.1.7" -itertools = "0.12.0" +itertools = "0.12.1" line-index = { path = "../line-index" } log = "0.4.21" multimap = "0.10.0" once_cell = "1.19.0" -regex = "1.10.2" +regex = "1.10.4" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } url = "2.5.0" [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/diagnostics/src/citations.rs b/support/texlab/crates/diagnostics/src/citations.rs index 84b9a10b1c..0f09f589be 100644 --- a/support/texlab/crates/diagnostics/src/citations.rs +++ b/support/texlab/crates/diagnostics/src/citations.rs @@ -24,7 +24,7 @@ pub fn detect_undefined_citations<'a>( for citation in &data.semantics.citations { let name = citation.name_text(); - if name != "*" && !entries.contains(name) { + if name != "*" && !entries.contains(name) && !name.contains("#") { let diagnostic = Diagnostic::Tex(citation.name.range, TexError::UndefinedCitation); results .entry(document.uri.clone()) diff --git a/support/texlab/crates/diagnostics/src/manager.rs b/support/texlab/crates/diagnostics/src/manager.rs index 81347410c1..3573bc40f2 100644 --- a/support/texlab/crates/diagnostics/src/manager.rs +++ b/support/texlab/crates/diagnostics/src/manager.rs @@ -1,6 +1,6 @@ use base_db::{deps::Project, util::filter_regex_patterns, Document, Owner, Workspace}; use multimap::MultiMap; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use url::Url; use crate::types::Diagnostic; @@ -33,6 +33,18 @@ impl Manager { self.chktex.insert(uri, diagnostics); } + /// Removes stale diagnostics for documents that are no longer part of the workspace. + pub fn cleanup(&mut self, workspace: &Workspace) { + let uris = workspace + .iter() + .map(|doc| &doc.uri) + .collect::>(); + + self.grammar.retain(|uri, _| uris.contains(uri)); + self.chktex.retain(|uri, _| uris.contains(uri)); + self.build_log.retain(|uri, _| uris.contains(uri)); + } + /// Returns all filtered diagnostics for the given workspace. pub fn get(&self, workspace: &Workspace) -> FxHashMap> { let mut results: FxHashMap> = FxHashMap::default(); diff --git a/support/texlab/crates/distro/Cargo.toml b/support/texlab/crates/distro/Cargo.toml index c3a03105c7..eefcea2b49 100644 --- a/support/texlab/crates/distro/Cargo.toml +++ b/support/texlab/crates/distro/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -anyhow = "1.0.72" +anyhow = "1.0.82" rustc-hash = "1.1.0" [lib] diff --git a/support/texlab/crates/folding/Cargo.toml b/support/texlab/crates/folding/Cargo.toml index 500e80f5b2..684ddeb1b9 100644 --- a/support/texlab/crates/folding/Cargo.toml +++ b/support/texlab/crates/folding/Cargo.toml @@ -12,7 +12,7 @@ rowan = "0.15.15" syntax = { path = "../syntax" } [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/highlights/Cargo.toml b/support/texlab/crates/highlights/Cargo.toml index a7f58d1d19..785c282e3a 100644 --- a/support/texlab/crates/highlights/Cargo.toml +++ b/support/texlab/crates/highlights/Cargo.toml @@ -11,7 +11,7 @@ base-db = { path = "../base-db" } rowan = "0.15.15" [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/hover/Cargo.toml b/support/texlab/crates/hover/Cargo.toml index ebc2522c08..946ed734fe 100644 --- a/support/texlab/crates/hover/Cargo.toml +++ b/support/texlab/crates/hover/Cargo.toml @@ -15,7 +15,7 @@ rowan = "0.15.15" syntax = { path = "../syntax" } [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/inlay-hints/Cargo.toml b/support/texlab/crates/inlay-hints/Cargo.toml index 5547f5649a..ede4ca6188 100644 --- a/support/texlab/crates/inlay-hints/Cargo.toml +++ b/support/texlab/crates/inlay-hints/Cargo.toml @@ -13,7 +13,7 @@ rustc-hash = "1.1.0" [dev-dependencies] test-utils = { path = "../test-utils" } -expect-test = "1.4.1" +expect-test = "1.5.0" [lib] doctest = false diff --git a/support/texlab/crates/ipc/Cargo.toml b/support/texlab/crates/ipc/Cargo.toml new file mode 100644 index 0000000000..ae31dcaf55 --- /dev/null +++ b/support/texlab/crates/ipc/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "ipc" +version = "0.0.0" +license.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +crossbeam-channel = "0.5.12" +serde = "1.0.199" +serde_json = "1.0.116" +log = "0.4.21" +uds_windows = "1.1.0" + +[lib] +doctest = false + +[dev-dependencies] +expect-test = "1.5.0" +parser = { path = "../parser" } diff --git a/support/texlab/crates/ipc/src/lib.rs b/support/texlab/crates/ipc/src/lib.rs new file mode 100644 index 0000000000..3da3239064 --- /dev/null +++ b/support/texlab/crates/ipc/src/lib.rs @@ -0,0 +1,54 @@ +use std::{ + io::{self, BufRead, BufReader, BufWriter, Read}, + path::PathBuf, +}; + +use serde::{de::DeserializeOwned, Serialize}; + +#[cfg(unix)] +use std::os::unix::net::{UnixListener, UnixStream}; + +#[cfg(windows)] +use uds_windows::{UnixListener, UnixStream}; + +fn socket_path() -> PathBuf { + std::env::temp_dir().join("texlab.sock") +} + +pub fn send_request(msg: T) -> io::Result<()> { + let stream = UnixStream::connect(socket_path())?; + let mut conn = BufWriter::new(stream); + serde_json::to_writer(&mut conn, &msg)?; + Ok(()) +} + +pub fn spawn_server(mut event_handler: F) -> io::Result<()> +where + T: DeserializeOwned, + F: FnMut(T) + Send + 'static, +{ + let socket_path = socket_path(); + let _ = std::fs::remove_file(&socket_path); + let listener = UnixListener::bind(socket_path)?; + + std::thread::spawn(move || { + for conn in listener.incoming().flatten() { + let _ = handle_request(conn, &mut event_handler); + } + }); + + Ok(()) +} + +fn handle_request(conn: impl Read, event_handler: &mut F) -> io::Result<()> +where + T: DeserializeOwned, + F: FnMut(T), +{ + let mut conn = BufReader::new(conn); + let mut line = String::new(); + conn.read_line(&mut line)?; + let msg = serde_json::from_str(&line)?; + event_handler(msg); + Ok(()) +} diff --git a/support/texlab/crates/links/Cargo.toml b/support/texlab/crates/links/Cargo.toml index d3df0c95f8..74378483a9 100644 --- a/support/texlab/crates/links/Cargo.toml +++ b/support/texlab/crates/links/Cargo.toml @@ -11,7 +11,7 @@ base-db = { path = "../base-db" } [dev-dependencies] test-utils = { path = "../test-utils" } -expect-test = "1.4.1" +expect-test = "1.5.0" [lib] doctest = false diff --git a/support/texlab/crates/parser/Cargo.toml b/support/texlab/crates/parser/Cargo.toml index 0fd8889899..39fa34ef63 100644 --- a/support/texlab/crates/parser/Cargo.toml +++ b/support/texlab/crates/parser/Cargo.toml @@ -7,16 +7,19 @@ edition.workspace = true rust-version.workspace = true [dependencies] -logos = "0.13.0" +log = "0.4.21" +logos = "0.14.0" once_cell = "1.19.0" -regex = "1.10.2" +pathdiff = "0.2.1" +regex = "1.10.4" rowan = "0.15.15" rustc-hash = "1.1.0" syntax = { path = "../syntax" } tempfile = "3.10.1" +versions = "6.2.0" [dev-dependencies] -expect-test = "1.4.1" +expect-test = "1.5.0" [lib] doctest = false diff --git a/support/texlab/crates/parser/src/bibtex.rs b/support/texlab/crates/parser/src/bibtex.rs index fda0d4a0ad..e2cde27dec 100644 --- a/support/texlab/crates/parser/src/bibtex.rs +++ b/support/texlab/crates/parser/src/bibtex.rs @@ -325,7 +325,7 @@ enum ValueToken { #[token("\"")] Quote, - #[regex(r"\d+", priority = 2)] + #[regex(r"\d+", priority = 3)] Integer, #[regex(r#"[^\s"\{\},#]+"#)] @@ -349,7 +349,7 @@ enum ContentToken { #[token("\"")] Quote, - #[regex(r"\d+", priority = 2)] + #[regex(r"\d+", priority = 3)] Integer, #[token(r#"~"#)] diff --git a/support/texlab/crates/parser/src/config.rs b/support/texlab/crates/parser/src/config.rs index 6537d21b0d..f81083f132 100644 --- a/support/texlab/crates/parser/src/config.rs +++ b/support/texlab/crates/parser/src/config.rs @@ -7,6 +7,7 @@ pub struct SyntaxConfig { pub enum_environments: FxHashSet, pub verbatim_environments: FxHashSet, pub citation_commands: FxHashSet, + pub label_definition_commands: FxHashSet, pub label_reference_commands: FxHashSet, } @@ -32,6 +33,11 @@ impl Default for SyntaxConfig { .map(ToString::to_string) .collect(); + let label_definition_commands = DEFAULT_LABEL_DEFINITION_COMMANDS + .iter() + .map(ToString::to_string) + .collect(); + let label_reference_commands = DEFAULT_LABEL_REFERENCE_COMMANDS .iter() .map(ToString::to_string) @@ -43,6 +49,7 @@ impl Default for SyntaxConfig { enum_environments, verbatim_environments, citation_commands, + label_definition_commands, label_reference_commands, } } @@ -163,6 +170,8 @@ static DEFAULT_CITATION_COMMANDS: &[&str] = &[ "citeA*", ]; +static DEFAULT_LABEL_DEFINITION_COMMANDS: &[&str] = &["label"]; + static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "ref", "vref", diff --git a/support/texlab/crates/parser/src/latex.rs b/support/texlab/crates/parser/src/latex.rs index a114d3ecba..669ba82f99 100644 --- a/support/texlab/crates/parser/src/latex.rs +++ b/support/texlab/crates/parser/src/latex.rs @@ -139,7 +139,8 @@ impl<'a> Parser<'a> { CommandName::LabelReference => self.label_reference(), CommandName::LabelReferenceRange => self.label_reference_range(), CommandName::LabelNumber => self.label_number(), - CommandName::CommandDefinition => self.command_definition(), + CommandName::OldCommandDefinition => self.old_command_definition(), + CommandName::NewCommandDefinition => self.new_command_definition(), CommandName::MathOperator => self.math_operator(), CommandName::GlossaryEntryDefinition => self.glossary_entry_definition(), CommandName::GlossaryEntryReference => self.glossary_entry_reference(), @@ -907,8 +908,21 @@ impl<'a> Parser<'a> { self.builder.finish_node(); } - fn command_definition(&mut self) { - self.builder.start_node(COMMAND_DEFINITION.into()); + fn old_command_definition(&mut self) { + self.builder.start_node(OLD_COMMAND_DEFINITION.into()); + self.eat(); + self.trivia(); + + if let Some(Token::CommandName(_)) = self.lexer.peek() { + self.eat(); + self.trivia(); + } + + self.builder.finish_node(); + } + + fn new_command_definition(&mut self) { + self.builder.start_node(NEW_COMMAND_DEFINITION.into()); self.eat(); self.trivia(); @@ -1071,7 +1085,7 @@ impl<'a> Parser<'a> { } if self.lexer.peek() == Some(Token::LCurly) { - self.curly_group_word(); + self.curly_group_word_list(); } self.builder.finish_node(); diff --git a/support/texlab/crates/parser/src/latex/lexer/commands.rs b/support/texlab/crates/parser/src/latex/lexer/commands.rs index 95eef4becd..4d1a7ca320 100644 --- a/support/texlab/crates/parser/src/latex/lexer/commands.rs +++ b/support/texlab/crates/parser/src/latex/lexer/commands.rs @@ -29,15 +29,26 @@ pub fn classify(name: &str, config: &SyntaxConfig) -> CommandName { "import" | "subimport" | "inputfrom" | "subinputfrom" | "subincludefrom" => { CommandName::Import } - "label" => CommandName::LabelDefinition, "crefrange" | "crefrange*" | "Crefrange" | "Crefrange*" => CommandName::LabelReferenceRange, "newlabel" => CommandName::LabelNumber, + "def" | "let" => CommandName::OldCommandDefinition, "newcommand" | "newcommand*" | "renewcommand" | "renewcommand*" | "DeclareRobustCommand" - | "DeclareRobustCommand*" => CommandName::CommandDefinition, + | "DeclareRobustCommand*" + | "NewDocumentCommand" + | "RenewDocumentCommand" + | "ProvideDocumentCommand" + | "DeclareDocumentCommand" + | "NewExpandableDocumentCommand" + | "RenewExpandableDocumentCommand" + | "ProvideExpandableDocumentCommand" + | "DeclareExpandableDocumentCommand" + | "NewCommandCopy" + | "RenewCommandCopy" + | "DeclareCommandCopy" => CommandName::NewCommandDefinition, "DeclareMathOperator" | "DeclareMathOperator*" => CommandName::MathOperator, "newglossaryentry" => CommandName::GlossaryEntryDefinition, "gls" | "Gls" | "GLS" | "glspl" | "Glspl" | "GLSpl" | "glsdisp" | "glslink" | "glstext" @@ -70,14 +81,24 @@ pub fn classify(name: &str, config: &SyntaxConfig) -> CommandName { "definecolor" => CommandName::ColorDefinition, "definecolorset" => CommandName::ColorSetDefinition, "usepgflibrary" | "usetikzlibrary" => CommandName::TikzLibraryImport, - "newenvironment" | "newenvironment*" | "renewenvironment" | "renewenvironment*" => { - CommandName::EnvironmentDefinition - } + "newenvironment" + | "newenvironment*" + | "renewenvironment" + | "renewenvironment*" + | "NewDocumentEnvironment" + | "RenewDocumentEnvironment" + | "ProvideDocumentEnvironment" + | "DeclareDocumentEnvironment" + | "NewEnvironmentCopy" + | "RenewEnvironmentCopy" + | "DeclareEnvironmentCopy" => CommandName::EnvironmentDefinition, "graphicspath" => CommandName::GraphicsPath, "iffalse" => CommandName::BeginBlockComment, "fi" => CommandName::EndBlockComment, "verb" => CommandName::VerbatimBlock, + _ if config.citation_commands.contains(name) => CommandName::Citation, + _ if config.label_definition_commands.contains(name) => CommandName::LabelDefinition, _ if config.label_reference_commands.contains(name) => CommandName::LabelReference, _ => CommandName::Generic, } diff --git a/support/texlab/crates/parser/src/latex/lexer/types.rs b/support/texlab/crates/parser/src/latex/lexer/types.rs index 27ae874d9d..896ce01c05 100644 --- a/support/texlab/crates/parser/src/latex/lexer/types.rs +++ b/support/texlab/crates/parser/src/latex/lexer/types.rs @@ -119,7 +119,8 @@ pub enum CommandName { LabelReference, LabelReferenceRange, LabelNumber, - CommandDefinition, + OldCommandDefinition, + NewCommandDefinition, MathOperator, GlossaryEntryDefinition, GlossaryEntryReference, diff --git a/support/texlab/crates/parser/src/latex/tests.rs b/support/texlab/crates/parser/src/latex/tests.rs index 9c086c43fd..2c9dc9335f 100644 --- a/support/texlab/crates/parser/src/latex/tests.rs +++ b/support/texlab/crates/parser/src/latex/tests.rs @@ -560,21 +560,21 @@ fn test_command_definition_no_argc() { check( r#"\newcommand{\foo}{foo}"#, expect![[r#" - ROOT@0..22 - PREAMBLE@0..22 - COMMAND_DEFINITION@0..22 - COMMAND_NAME@0..11 "\\newcommand" - CURLY_GROUP_COMMAND@11..17 - L_CURLY@11..12 "{" - COMMAND_NAME@12..16 "\\foo" - R_CURLY@16..17 "}" - CURLY_GROUP@17..22 - L_CURLY@17..18 "{" - TEXT@18..21 - WORD@18..21 "foo" - R_CURLY@21..22 "}" + ROOT@0..22 + PREAMBLE@0..22 + NEW_COMMAND_DEFINITION@0..22 + COMMAND_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..17 + L_CURLY@11..12 "{" + COMMAND_NAME@12..16 "\\foo" + R_CURLY@16..17 "}" + CURLY_GROUP@17..22 + L_CURLY@17..18 "{" + TEXT@18..21 + WORD@18..21 "foo" + R_CURLY@21..22 "}" - "#]], + "#]], ); } @@ -583,16 +583,16 @@ fn test_command_definition_no_impl() { check( r#"\newcommand{\foo}"#, expect![[r#" - ROOT@0..17 - PREAMBLE@0..17 - COMMAND_DEFINITION@0..17 - COMMAND_NAME@0..11 "\\newcommand" - CURLY_GROUP_COMMAND@11..17 - L_CURLY@11..12 "{" - COMMAND_NAME@12..16 "\\foo" - R_CURLY@16..17 "}" + ROOT@0..17 + PREAMBLE@0..17 + NEW_COMMAND_DEFINITION@0..17 + COMMAND_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..17 + L_CURLY@11..12 "{" + COMMAND_NAME@12..16 "\\foo" + R_CURLY@16..17 "}" - "#]], + "#]], ); } @@ -601,15 +601,15 @@ fn test_command_definition_no_impl_error() { check( r#"\newcommand{\foo"#, expect![[r#" - ROOT@0..16 - PREAMBLE@0..16 - COMMAND_DEFINITION@0..16 - COMMAND_NAME@0..11 "\\newcommand" - CURLY_GROUP_COMMAND@11..16 - L_CURLY@11..12 "{" - COMMAND_NAME@12..16 "\\foo" + ROOT@0..16 + PREAMBLE@0..16 + NEW_COMMAND_DEFINITION@0..16 + COMMAND_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..16 + L_CURLY@11..12 "{" + COMMAND_NAME@12..16 "\\foo" - "#]], + "#]], ); } @@ -618,31 +618,31 @@ fn test_command_definition_optional() { check( r#"\newcommand{\foo}[1][def]{#1}"#, expect![[r##" - ROOT@0..29 - PREAMBLE@0..29 - COMMAND_DEFINITION@0..29 - COMMAND_NAME@0..11 "\\newcommand" - CURLY_GROUP_COMMAND@11..17 - L_CURLY@11..12 "{" - COMMAND_NAME@12..16 "\\foo" - R_CURLY@16..17 "}" - BRACK_GROUP_WORD@17..20 - L_BRACK@17..18 "[" - KEY@18..19 - WORD@18..19 "1" - R_BRACK@19..20 "]" - BRACK_GROUP@20..25 - L_BRACK@20..21 "[" - TEXT@21..24 - WORD@21..24 "def" - R_BRACK@24..25 "]" - CURLY_GROUP@25..29 - L_CURLY@25..26 "{" - TEXT@26..28 - WORD@26..28 "#1" - R_CURLY@28..29 "}" + ROOT@0..29 + PREAMBLE@0..29 + NEW_COMMAND_DEFINITION@0..29 + COMMAND_NAME@0..11 "\\newcommand" + CURLY_GROUP_COMMAND@11..17 + L_CURLY@11..12 "{" + COMMAND_NAME@12..16 "\\foo" + R_CURLY@16..17 "}" + BRACK_GROUP_WORD@17..20 + L_BRACK@17..18 "[" + KEY@18..19 + WORD@18..19 "1" + R_BRACK@19..20 "]" + BRACK_GROUP@20..25 + L_BRACK@20..21 "[" + TEXT@21..24 + WORD@21..24 "def" + R_BRACK@24..25 "]" + CURLY_GROUP@25..29 + L_CURLY@25..26 "{" + TEXT@26..28 + WORD@26..28 "#1" + R_CURLY@28..29 "}" - "##]], + "##]], ); } @@ -651,27 +651,27 @@ fn test_command_definition_simple() { check( r#"\newcommand[1]{\id}{#1}"#, expect![[r##" - ROOT@0..23 - PREAMBLE@0..23 - COMMAND_DEFINITION@0..19 - COMMAND_NAME@0..11 "\\newcommand" - BRACK_GROUP_WORD@11..14 - L_BRACK@11..12 "[" - KEY@12..13 - WORD@12..13 "1" - R_BRACK@13..14 "]" - CURLY_GROUP@14..19 - L_CURLY@14..15 "{" - GENERIC_COMMAND@15..18 - COMMAND_NAME@15..18 "\\id" - R_CURLY@18..19 "}" - CURLY_GROUP@19..23 - L_CURLY@19..20 "{" - TEXT@20..22 - WORD@20..22 "#1" - R_CURLY@22..23 "}" + ROOT@0..23 + PREAMBLE@0..23 + NEW_COMMAND_DEFINITION@0..19 + COMMAND_NAME@0..11 "\\newcommand" + BRACK_GROUP_WORD@11..14 + L_BRACK@11..12 "[" + KEY@12..13 + WORD@12..13 "1" + R_BRACK@13..14 "]" + CURLY_GROUP@14..19 + L_CURLY@14..15 "{" + GENERIC_COMMAND@15..18 + COMMAND_NAME@15..18 "\\id" + R_CURLY@18..19 "}" + CURLY_GROUP@19..23 + L_CURLY@19..20 "{" + TEXT@20..22 + WORD@20..22 "#1" + R_CURLY@22..23 "}" - "##]], + "##]], ); } @@ -682,7 +682,7 @@ fn test_command_definition_with_begin() { expect![[r#" ROOT@0..80 PREAMBLE@0..80 - COMMAND_DEFINITION@0..80 + NEW_COMMAND_DEFINITION@0..80 COMMAND_NAME@0..11 "\\newcommand" CURLY_GROUP_COMMAND@11..35 L_CURLY@11..12 "{" @@ -2446,11 +2446,11 @@ fn test_issue_745() { GENERIC_COMMAND@50..64 COMMAND_NAME@50..63 "\\ExplSyntaxOn" LINE_BREAK@63..64 "\n" - GENERIC_COMMAND@64..223 + ENVIRONMENT_DEFINITION@64..200 COMMAND_NAME@64..87 "\\NewDocumentEnvironment" - CURLY_GROUP@87..96 + CURLY_GROUP_WORD@87..96 L_CURLY@87..88 "{" - TEXT@88..95 + KEY@88..95 WORD@88..95 "exptblr" R_CURLY@95..96 "}" CURLY_GROUP@96..107 @@ -2515,21 +2515,21 @@ fn test_issue_745() { R_CURLY@194..195 "}" LINE_BREAK@195..196 "\n" WHITESPACE@196..200 " " - CURLY_GROUP@200..223 - L_CURLY@200..201 "{" - LINE_BREAK@201..202 "\n" - WHITESPACE@202..206 " " - GENERIC_COMMAND@206..221 - COMMAND_NAME@206..210 "\\end" - CURLY_GROUP@210..221 - L_CURLY@210..211 "{" - TEXT@211..215 - WORD@211..215 "tblr" - R_CURLY@215..216 "}" - LINE_BREAK@216..217 "\n" - WHITESPACE@217..221 " " - R_CURLY@221..222 "}" - LINE_BREAK@222..223 "\n" + CURLY_GROUP@200..223 + L_CURLY@200..201 "{" + LINE_BREAK@201..202 "\n" + WHITESPACE@202..206 " " + GENERIC_COMMAND@206..221 + COMMAND_NAME@206..210 "\\end" + CURLY_GROUP@210..221 + L_CURLY@210..211 "{" + TEXT@211..215 + WORD@211..215 "tblr" + R_CURLY@215..216 "}" + LINE_BREAK@216..217 "\n" + WHITESPACE@217..221 " " + R_CURLY@221..222 "}" + LINE_BREAK@222..223 "\n" GENERIC_COMMAND@223..239 COMMAND_NAME@223..237 "\\ExplSyntaxOff" LINE_BREAK@237..239 "\n\n" @@ -2681,7 +2681,7 @@ fn test_issue_857() { expect![[r#" ROOT@0..55 PREAMBLE@0..55 - COMMAND_DEFINITION@0..11 + NEW_COMMAND_DEFINITION@0..11 COMMAND_NAME@0..11 "\\newcommand" GENERIC_COMMAND@11..17 COMMAND_NAME@11..14 "\\ö" @@ -2689,7 +2689,7 @@ fn test_issue_857() { L_CURLY@14..15 "{" R_CURLY@15..16 "}" LINE_BREAK@16..17 "\n" - COMMAND_DEFINITION@17..38 + NEW_COMMAND_DEFINITION@17..38 COMMAND_NAME@17..28 "\\newcommand" CURLY_GROUP_COMMAND@28..35 L_CURLY@28..29 "{" @@ -2699,7 +2699,7 @@ fn test_issue_857() { L_CURLY@35..36 "{" R_CURLY@36..37 "}" LINE_BREAK@37..38 "\n" - COMMAND_DEFINITION@38..49 + NEW_COMMAND_DEFINITION@38..49 COMMAND_NAME@38..49 "\\newcommand" GENERIC_COMMAND@49..55 COMMAND_NAME@49..53 "\\123" @@ -3536,7 +3536,7 @@ fn test_theorem_definition_thmtools() { TEXT@32..35 WORD@32..35 "bar" R_BRACK@35..36 "]" - CURLY_GROUP_WORD@36..41 + CURLY_GROUP_WORD_LIST@36..41 L_CURLY@36..37 "{" KEY@37..40 WORD@37..40 "baz" @@ -3546,6 +3546,61 @@ fn test_theorem_definition_thmtools() { ); } +#[test] +fn test_theorem_definition_thmtools_multiple() { + check( + r#"\declaretheorem[sibling=table, style=thmbox]{definition, theorem, lemma, corollary, proposition}"#, + expect![[r#" + ROOT@0..96 + PREAMBLE@0..96 + THEOREM_DEFINITION_THMTOOLS@0..96 + COMMAND_NAME@0..15 "\\declaretheorem" + BRACK_GROUP_KEY_VALUE@15..44 + L_BRACK@15..16 "[" + KEY_VALUE_BODY@16..43 + KEY_VALUE_PAIR@16..29 + KEY@16..23 + WORD@16..23 "sibling" + EQUALITY_SIGN@23..24 "=" + VALUE@24..29 + TEXT@24..29 + WORD@24..29 "table" + COMMA@29..30 "," + WHITESPACE@30..31 " " + KEY_VALUE_PAIR@31..43 + KEY@31..36 + WORD@31..36 "style" + EQUALITY_SIGN@36..37 "=" + VALUE@37..43 + TEXT@37..43 + WORD@37..43 "thmbox" + R_BRACK@43..44 "]" + CURLY_GROUP_WORD_LIST@44..96 + L_CURLY@44..45 "{" + KEY@45..55 + WORD@45..55 "definition" + COMMA@55..56 "," + WHITESPACE@56..57 " " + KEY@57..64 + WORD@57..64 "theorem" + COMMA@64..65 "," + WHITESPACE@65..66 " " + KEY@66..71 + WORD@66..71 "lemma" + COMMA@71..72 "," + WHITESPACE@72..73 " " + KEY@73..82 + WORD@73..82 "corollary" + COMMA@82..83 "," + WHITESPACE@83..84 " " + KEY@84..95 + WORD@84..95 "proposition" + R_CURLY@95..96 "}" + + "#]], + ); +} + #[test] fn test_command_subscript() { check( diff --git a/support/texlab/crates/parser/src/latexmkrc.rs b/support/texlab/crates/parser/src/latexmkrc.rs index 63c92bd353..6013a5f646 100644 --- a/support/texlab/crates/parser/src/latexmkrc.rs +++ b/support/texlab/crates/parser/src/latexmkrc.rs @@ -1,52 +1,157 @@ +use std::path::{Path, PathBuf}; + use syntax::latexmkrc::LatexmkrcData; -use tempfile::tempdir; - -pub fn parse_latexmkrc(_input: &str) -> std::io::Result { - let temp_dir = tempdir()?; - let non_existent_tex = temp_dir.path().join("NONEXISTENT.tex"); - - // Run `latexmk -dir-report $TMPDIR/NONEXISTENT.tex` to obtain out_dir - // and aux_dir values. We pass nonexistent file to prevent latexmk from - // building anything, since we need this invocation only to extract the - // -dir-report variables. - // - // In the future, latexmk plans to implement -dir-report-only option and we - // won't have to resort to this hack with NONEXISTENT.tex. - let output = std::process::Command::new("latexmk") - .arg("-dir-report") - .arg(non_existent_tex) - .output()?; - let stderr = String::from_utf8_lossy(&output.stderr); +mod v483 { + use std::path::Path; + + use syntax::latexmkrc::LatexmkrcData; + use tempfile::tempdir; + + use crate::latexmkrc::change_root; + + pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { + let temp_dir = tempdir()?; + let non_existent_tex = temp_dir.path().join("NONEXISTENT.tex"); + std::fs::write(temp_dir.path().join(".latexmkrc"), input)?; + + // Run `latexmk -dir-report $TMPDIR/NONEXISTENT.tex` to obtain out_dir + // and aux_dir values. We pass nonexistent file to prevent latexmk from + // building anything, since we need this invocation only to extract the + // -dir-report variables. + // + // In later versions, latexmk provides the -dir-report-only option and we + // won't have to resort to this hack with NONEXISTENT.tex. + let output = std::process::Command::new("latexmk") + .arg("-dir-report") + .arg(non_existent_tex) + .current_dir(temp_dir.path()) + .output()?; + + let stderr = String::from_utf8_lossy(&output.stderr); + + let (aux_dir, out_dir) = stderr.lines().find_map(extract_dirs).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Normalized aux and out dir were not found in latexmk output", + ) + })?; + + let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir); + let out_dir = change_root(src_dir, temp_dir.path(), &out_dir); + Ok(LatexmkrcData { aux_dir, out_dir }) + } + + /// Extracts $aux_dir and $out_dir from lines of the form + /// + /// Latexmk: Normalized aux dir and out dir: '$aux_dir', '$out_dir' + fn extract_dirs(line: &str) -> Option<(String, String)> { + let mut it = line + .strip_prefix("Latexmk: Normalized aux dir and out dir: ")? + .split(", "); + + let aux_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; + let out_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; + + // Ensure there's no more data + if it.next().is_some() { + return None; + } + + Some((String::from(aux_dir), String::from(out_dir))) + } +} + +mod v484 { + use std::{path::Path, str::Lines}; + + use syntax::latexmkrc::LatexmkrcData; + use tempfile::tempdir; - let (aux_dir, out_dir) = stderr.lines().find_map(extract_dirs).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Normalized aux and out dir were not found in latexmk output", - ) - })?; + use super::change_root; - Ok(LatexmkrcData { - aux_dir: Some(aux_dir), - out_dir: Some(out_dir), - }) + pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { + let temp_dir = tempdir()?; + std::fs::write(temp_dir.path().join(".latexmkrc"), input)?; + + // Create an empty dummy TeX file to let latexmk continue + std::fs::write(temp_dir.path().join("dummy.tex"), "")?; + + // Run `latexmk -dir-report-only` to obtain out_dir and aux_dir values. + let output = std::process::Command::new("latexmk") + .arg("-dir-report-only") + .current_dir(temp_dir.path()) + .output()?; + + let stdout = String::from_utf8_lossy(&output.stdout); + + let (aux_dir, out_dir) = extract_dirs(stdout.lines()).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Normalized aux and out dir were not found in latexmk output", + ) + })?; + + let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir); + let out_dir = change_root(src_dir, temp_dir.path(), &out_dir); + + Ok(LatexmkrcData { aux_dir, out_dir }) + } + + /// Extracts $aux_dir and $out_dir from lines of the form + /// + /// Latexmk: Normalized aux dir and out dirs: + /// '$aux_dir', '$out_dir', [...] + fn extract_dirs(lines: Lines) -> Option<(String, String)> { + let mut it = lines + .skip_while(|line| !line.starts_with("Latexmk: Normalized aux dir and out dirs:")) + .nth(1)? + .split(","); + + let aux_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; + + it.next(); // Skip the old 'outdir' option. + + let out_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; + + // Ensure there's no more data + if it.next().is_some() { + return None; + } + + Some((String::from(aux_dir), String::from(out_dir))) + } } -/// Extracts $aux_dir and $out_dir from lines of the form -/// -/// Latexmk: Normalized aux dir and out dir: '$aux_dir', '$out_dir' -fn extract_dirs(line: &str) -> Option<(String, String)> { - let mut it = line - .strip_prefix("Latexmk: Normalized aux dir and out dir: ")? - .split(", "); +pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { + let output = std::process::Command::new("latexmk") + .arg("--version") + .output()?; + + let version = String::from_utf8(output.stdout) + .ok() + .as_ref() + .and_then(|line| Some((line.find("Version")?, line))) + .and_then(|(i, line)| line[i..].trim_end().strip_prefix("Version ")) + .and_then(|text| versions::Versioning::new(text)); - let aux_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; - let out_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; + let result = if version.map_or(false, |v| v >= versions::Versioning::new("4.84").unwrap()) { + v484::parse_latexmkrc(input, src_dir) + } else { + v483::parse_latexmkrc(input, src_dir) + }; + + log::debug!("Latexmkrc parsing result: src_dir={src_dir:?}, output={result:?}"); + result +} - // Ensure there's no more data - if it.next().is_some() { +fn change_root(src_dir: &Path, tmp_dir: &Path, out_dir: &str) -> Option { + let out_dir = tmp_dir.join(out_dir); + let relative_to_tmp = pathdiff::diff_paths(out_dir, tmp_dir)?; + let relative_to_src = pathdiff::diff_paths(src_dir.join(relative_to_tmp), src_dir)?; + if relative_to_src == PathBuf::new() { return None; } - Some((String::from(aux_dir), String::from(out_dir))) + Some(relative_to_src.to_str()?.to_string()) } diff --git a/support/texlab/crates/references/Cargo.toml b/support/texlab/crates/references/Cargo.toml index b2d80d49ee..e78e84ea4c 100644 --- a/support/texlab/crates/references/Cargo.toml +++ b/support/texlab/crates/references/Cargo.toml @@ -9,6 +9,7 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.15" +rustc-hash = "1.1.0" syntax = { path = "../syntax" } [dev-dependencies] diff --git a/support/texlab/crates/references/src/command.rs b/support/texlab/crates/references/src/command.rs new file mode 100644 index 0000000000..712da20b73 --- /dev/null +++ b/support/texlab/crates/references/src/command.rs @@ -0,0 +1,52 @@ +use base_db::{semantics::Span, DocumentLocation}; +use rowan::ast::AstNode; +use rustc_hash::FxHashSet; +use syntax::latex; + +use crate::{Reference, ReferenceContext, ReferenceKind}; + +pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> { + let data = context.params.feature.document.data.as_tex()?; + let token = data + .root_node() + .token_at_offset(context.params.offset) + .find(|token| token.kind() == latex::COMMAND_NAME)?; + + let project = &context.params.feature.project; + + for document in &project.documents { + if let Some(data) = document.data.as_tex() { + let defs: FxHashSet = data + .root_node() + .descendants() + .filter_map(|node| { + latex::OldCommandDefinition::cast(node.clone()) + .and_then(|node| node.name()) + .or_else(|| { + latex::NewCommandDefinition::cast(node) + .and_then(|node| node.name()) + .and_then(|group| group.command()) + }) + .map(|name| Span::command(&name)) + }) + .collect(); + + for command in &data.semantics.commands { + if command.text == &token.text()[1..] { + let kind = if defs.contains(command) { + ReferenceKind::Definition + } else { + ReferenceKind::Reference + }; + + context.results.push(Reference { + location: DocumentLocation::new(document, command.range), + kind, + }); + } + } + } + } + + Some(()) +} diff --git a/support/texlab/crates/references/src/lib.rs b/support/texlab/crates/references/src/lib.rs index 23dbd7e0ae..7d14a20d25 100644 --- a/support/texlab/crates/references/src/lib.rs +++ b/support/texlab/crates/references/src/lib.rs @@ -1,3 +1,4 @@ +mod command; mod entry; mod label; mod string_def; @@ -39,6 +40,7 @@ pub fn find_all<'a>(params: &ReferenceParams<'a>) -> Vec> { entry::find_all(&mut context); label::find_all(&mut context); string_def::find_all(&mut context); + command::find_all(&mut context); context .results diff --git a/support/texlab/crates/references/src/tests.rs b/support/texlab/crates/references/src/tests.rs index a21a3ce551..845282afb0 100644 --- a/support/texlab/crates/references/src/tests.rs +++ b/support/texlab/crates/references/src/tests.rs @@ -229,3 +229,34 @@ fn test_string_definition_include_decl() { true, ); } + +#[test] +fn test_new_command_definition() { + check( + r#" +%! main.tex +\foo + | + ^^^ + +\newcommand{\foo}{foo} +"#, + false, + ); +} + +#[test] +fn test_new_command_definition_include_decl() { + check( + r#" +%! main.tex +\foo + | + ^^^ + +\newcommand{\foo}{foo} + ^^^ +"#, + true, + ); +} diff --git a/support/texlab/crates/symbols/Cargo.toml b/support/texlab/crates/symbols/Cargo.toml index d297703d3b..65db0fc0bd 100644 --- a/support/texlab/crates/symbols/Cargo.toml +++ b/support/texlab/crates/symbols/Cargo.toml @@ -12,14 +12,14 @@ doctest = false [dependencies] base-db = { path = "../base-db" } distro = { path = "../distro" } -itertools = "0.12.0" +itertools = "0.12.1" line-index = { path = "../line-index" } rowan = "0.15.15" syntax = { path = "../syntax" } -titlecase = "2.2.1" +titlecase = "3.0.0" url = "2.5.0" [dev-dependencies] -regex = "1.10.2" +regex = "1.10.4" test-utils = { path = "../test-utils" } -expect-test = "1.4.1" +expect-test = "1.5.0" diff --git a/support/texlab/crates/syntax/Cargo.toml b/support/texlab/crates/syntax/Cargo.toml index aaad7e973e..a2d3d866b6 100644 --- a/support/texlab/crates/syntax/Cargo.toml +++ b/support/texlab/crates/syntax/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -itertools = "0.12.0" +itertools = "0.12.1" rowan = "0.15.15" [lib] diff --git a/support/texlab/crates/syntax/src/latex/cst.rs b/support/texlab/crates/syntax/src/latex/cst.rs index 95aa1e4667..f16d703162 100644 --- a/support/texlab/crates/syntax/src/latex/cst.rs +++ b/support/texlab/crates/syntax/src/latex/cst.rs @@ -555,8 +555,19 @@ impl TheoremDefinition { self.syntax().first_token() } - pub fn name(&self) -> Option { - self.syntax().children().find_map(CurlyGroupWord::cast) + pub fn names(&self) -> impl Iterator { + self.syntax() + .children() + .find_map(CurlyGroupWordList::cast) + .into_iter() + .flat_map(|group| group.keys()) + .chain( + self.syntax() + .children() + .find_map(CurlyGroupWord::cast) + .into_iter() + .filter_map(|group| group.key()), + ) } pub fn heading(&self) -> Option { @@ -581,9 +592,25 @@ impl TheoremDefinition { } } -cst_node!(CommandDefinition, COMMAND_DEFINITION, MATH_OPERATOR); +cst_node!(OldCommandDefinition, OLD_COMMAND_DEFINITION); + +impl OldCommandDefinition { + pub fn command(&self) -> Option { + self.syntax().first_token() + } + + pub fn name(&self) -> Option { + self.syntax() + .children_with_tokens() + .skip(1) + .filter_map(|elem| elem.into_token()) + .find(|token| token.kind() == COMMAND_NAME) + } +} + +cst_node!(NewCommandDefinition, NEW_COMMAND_DEFINITION, MATH_OPERATOR); -impl CommandDefinition { +impl NewCommandDefinition { pub fn command(&self) -> Option { self.syntax().first_token() } diff --git a/support/texlab/crates/syntax/src/latex/kind.rs b/support/texlab/crates/syntax/src/latex/kind.rs index 39e2837932..8b649b1f31 100644 --- a/support/texlab/crates/syntax/src/latex/kind.rs +++ b/support/texlab/crates/syntax/src/latex/kind.rs @@ -66,7 +66,8 @@ pub enum SyntaxKind { LABEL_REFERENCE, LABEL_REFERENCE_RANGE, LABEL_NUMBER, - COMMAND_DEFINITION, + OLD_COMMAND_DEFINITION, + NEW_COMMAND_DEFINITION, MATH_OPERATOR, GLOSSARY_ENTRY_DEFINITION, GLOSSARY_ENTRY_REFERENCE, diff --git a/support/texlab/crates/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml index 8fe5d4fbbe..4c142a2229 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.14.1" +version = "5.16.0" license.workspace = true readme = "README.md" authors.workspace = true @@ -22,11 +22,11 @@ test = false doctest = false [dependencies] -anyhow = "1.0.75" +anyhow = "1.0.82" base-db = { path = "../base-db" } bibfmt = { path = "../bibfmt" } citeproc = { path = "../citeproc" } -clap = { version = "4.5.3", features = ["derive"] } +clap = { version = "4.5.4", features = ["derive"] } commands = { path = "../commands" } completion = { path = "../completion" } completion-data = { path = "../completion-data" } @@ -39,22 +39,23 @@ folding = { path = "../folding" } highlights = { path = "../highlights" } hover = { path = "../hover" } inlay-hints = { path = "../inlay-hints" } +ipc = { path = "../ipc" } line-index = { path = "../line-index" } links = { path = "../links" } log = "0.4.21" lsp-server = "0.7.6" -lsp-types = "0.95.0" +lsp-types = "0.95.1" notify = "6.1.1" notify-debouncer-full = "0.3.1" parking_lot = "0.12.1" parser = { path = "../parser" } references = { path = "../references" } -regex = "1.10.2" +regex = "1.10.4" rename = { path = "../rename" } rowan = "0.15.15" rustc-hash = "1.1.0" serde = "1.0.195" -serde_json = "1.0.114" +serde_json = "1.0.115" serde_regex = "1.1.0" serde_repr = "0.1.18" symbols = { path = "../symbols" } diff --git a/support/texlab/crates/texlab/src/main.rs b/support/texlab/crates/texlab/src/main.rs index 08977ae96a..ceca02b7f7 100644 --- a/support/texlab/crates/texlab/src/main.rs +++ b/support/texlab/crates/texlab/src/main.rs @@ -1,9 +1,10 @@ use std::{fs::OpenOptions, io, path::PathBuf}; use anyhow::Result; -use clap::{ArgAction, Parser}; +use clap::{ArgAction, Parser, Subcommand}; use log::LevelFilter; use lsp_server::Connection; +use lsp_types::Url; use texlab::Server; /// An implementation of the Language Server Protocol for LaTeX @@ -21,20 +22,72 @@ struct Opts { /// Write the logging output to FILE #[clap(long, name = "FILE", value_parser)] log_file: Option, + + #[clap(subcommand)] + command: Option, +} + +#[derive(Debug, Subcommand)] +enum Command { + /// Runs the language server in a editor context using STDIN and STDOUT. + Run, + + /// Opens a document at a specific line. + /// + /// This command can be used to implement inverse search in an editor-agnostic way. + InverseSearch(InverseSearchOpts), +} + +/// Options for the inverse search subcommand. +#[derive(Debug, Parser)] +struct InverseSearchOpts { + /// The path to the document to open. + #[clap(short, long, name = "FILE", value_parser)] + input: PathBuf, + + /// The zero-based line number of the document to jump to. + #[clap(short, long)] + line: u32, } fn main() -> Result<()> { let opts = Opts::parse(); - setup_logger(opts); + setup_logger(&opts); + + match opts.command.unwrap_or(Command::Run) { + Command::Run => { + let (connection, threads) = Connection::stdio(); + Server::exec(connection)?; + threads.join()?; + } + Command::InverseSearch(opts) => { + let Some(uri) = opts + .input + .canonicalize() + .ok() + .and_then(|path| Url::from_file_path(path).ok()) + else { + eprintln!("Failed to convert input path to a URI."); + std::process::exit(-1); + }; + + let params = lsp_types::TextDocumentPositionParams::new( + lsp_types::TextDocumentIdentifier::new(uri), + lsp_types::Position::new(opts.line, 0), + ); - let (connection, threads) = Connection::stdio(); - Server::exec(connection)?; - threads.join()?; + if let Err(why) = ipc::send_request(params) { + eprintln!("Failed to send inverse search request to the main instance. Is the server running?"); + eprintln!("Details: {why:?}"); + std::process::exit(-1); + } + } + } Ok(()) } -fn setup_logger(opts: Opts) { +fn setup_logger(opts: &Opts) { let verbosity_level = if !opts.quiet { match opts.verbosity { 0 => LevelFilter::Error, @@ -52,7 +105,7 @@ fn setup_logger(opts: Opts) { .level(verbosity_level) .chain(io::stderr()); - let logger = match opts.log_file { + let logger = match &opts.log_file { Some(log_file) => logger.chain( OpenOptions::new() .write(true) diff --git a/support/texlab/crates/texlab/src/server.rs b/support/texlab/crates/texlab/src/server.rs index 70b9a03022..ef70c42c71 100644 --- a/support/texlab/crates/texlab/src/server.rs +++ b/support/texlab/crates/texlab/src/server.rs @@ -11,7 +11,7 @@ use std::{ }; use anyhow::Result; -use base_db::{deps, Config, Owner, Workspace}; +use base_db::{deps, Owner, Workspace}; use commands::{BuildCommand, CleanCommand, CleanTarget, ForwardSearch}; use crossbeam_channel::{Receiver, Sender}; use distro::{Distro, Language}; @@ -51,6 +51,7 @@ enum InternalMessage { Diagnostics, ChktexFinished(Url, Vec), ForwardSearch(Url, Option), + InverseSearch(TextDocumentPositionParams), } pub struct Server { @@ -242,7 +243,7 @@ impl Server { for document in checked_paths .iter() - .filter_map(|path| workspace.lookup_path(path)) + .filter_map(|path| workspace.lookup_file(path)) { self.diagnostic_manager.update_syntax(&workspace, document); } @@ -319,7 +320,7 @@ impl Server { fn update_options(&mut self, options: Options) { let mut workspace = self.workspace.write(); - workspace.set_config(Config::from(options)); + workspace.set_config(from_proto::config(options)); self.watcher.watch(&mut workspace); } @@ -794,30 +795,50 @@ impl Server { let mut changed = false; let mut workspace = self.workspace.write(); + match event.kind { notify::EventKind::Remove(_) | notify::EventKind::Modify(ModifyKind::Name(_)) => { - for path in event.paths { - if let Some(document) = workspace.lookup_path(&path) { - if document.owner == Owner::Server { - let uri = document.uri.clone(); - workspace.remove(&uri); - changed = true; - } - } + let affected_uris = event + .paths + .iter() + .flat_map(|file_or_dir| workspace.lookup_file_or_dir(file_or_dir)) + .filter(|doc| doc.owner == Owner::Server) + .map(|doc| doc.uri.clone()) + .collect::>(); + + for uri in affected_uris { + workspace.remove(&uri); + changed = true; } } notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { - for path in event.paths { - if workspace - .lookup_path(&path) - .map_or(true, |document| document.owner == Owner::Server) - { - if let Some(language) = Language::from_path(&path) { - changed |= workspace.load(&path, language).is_ok(); - - if let Some(document) = workspace.lookup_path(&path) { - self.diagnostic_manager.update_syntax(&workspace, document); - } + for file_or_dir in event.paths { + let affected_paths = if file_or_dir.is_dir() { + changed = true; + workspace + .lookup_file_or_dir(&file_or_dir) + .filter_map(|doc| doc.path.clone()) + .collect::>() + } else { + vec![file_or_dir] + }; + + for path in affected_paths { + if !workspace + .lookup_file(&path) + .map_or(true, |doc| doc.owner == Owner::Server) + { + continue; + } + + let Some(language) = Language::from_path(&path) else { + continue; + }; + + changed |= workspace.load(&path, language).is_ok(); + + if let Some(document) = workspace.lookup_file(&path) { + self.diagnostic_manager.update_syntax(&workspace, document); } } } @@ -825,9 +846,10 @@ impl Server { notify::EventKind::Any | notify::EventKind::Access(_) | notify::EventKind::Other => {} }; - drop(workspace); if changed { - self.publish_diagnostics_with_delay(); + self.diagnostic_manager.cleanup(&workspace); + drop(workspace); + self.update_workspace(); } } @@ -921,6 +943,27 @@ impl Server { Ok(results) } + fn inverse_search(&self, params: TextDocumentPositionParams) -> Result<()> { + if !self.client_flags.show_document { + log::warn!("Inverse search request received although the client does not support window/showDocument: {params:?}"); + } + + let position = lsp_types::Position::new(params.position.line, 0); + let params = lsp_types::ShowDocumentParams { + uri: params.text_document.uri, + take_focus: Some(true), + external: Some(false), + selection: Some(lsp_types::Range::new(position, position)), + }; + + let client = self.client.clone(); + self.pool.execute(move || { + let _ = client.send_request::(params); + }); + + Ok(()) + } + fn parse_command_params( &self, params: Vec, @@ -934,6 +977,13 @@ impl Server { Ok(value) } + fn setup_ipc_server(&mut self) { + let sender = self.internal_tx.clone(); + let _ = ipc::spawn_server(move |params: TextDocumentPositionParams| { + let _ = sender.send(InternalMessage::InverseSearch(params)); + }); + } + fn process_messages(&mut self) -> Result<()> { loop { crossbeam_channel::select! { @@ -1038,6 +1088,9 @@ impl Server { InternalMessage::ForwardSearch(uri, position) => { self.forward_search(None, uri, position)?; } + InternalMessage::InverseSearch(params) => { + self.inverse_search(params)?; + } }; } }; @@ -1060,6 +1113,7 @@ impl Server { self.register_configuration(); self.pull_options(); + self.setup_ipc_server(); self.process_messages()?; self.pool.join(); Ok(()) diff --git a/support/texlab/crates/texlab/src/server/options.rs b/support/texlab/crates/texlab/src/server/options.rs index 60c0bf90f7..02200392f6 100644 --- a/support/texlab/crates/texlab/src/server/options.rs +++ b/support/texlab/crates/texlab/src/server/options.rs @@ -1,6 +1,3 @@ -use std::time::Duration; - -use base_db::{Config, Formatter, SynctexConfig}; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -128,6 +125,7 @@ pub struct ExperimentalOptions { pub enum_environments: Vec, pub verbatim_environments: Vec, pub citation_commands: Vec, + pub label_definition_commands: Vec, pub label_reference_commands: Vec, } @@ -159,135 +157,3 @@ impl Default for CompletionMatcher { Self::FuzzyIgnoreCase } } - -impl From for Config { - fn from(value: Options) -> Self { - let mut config = Config::default(); - config.root_dir = value.root_directory; - - config.build.program = value.build.executable.unwrap_or(config.build.program); - config.build.args = value.build.args.unwrap_or(config.build.args); - config.build.on_save = value.build.on_save; - config.build.forward_search_after = value.build.forward_search_after; - - config.build.aux_dir = value - .build - .aux_directory - .or_else(|| value.aux_directory.clone()) - .unwrap_or_else(|| String::from(".")); - - config.build.pdf_dir = value - .build - .pdf_directory - .or(value.aux_directory) - .unwrap_or_else(|| String::from(".")); - - config.build.log_dir = value - .build - .log_directory - .unwrap_or_else(|| config.build.pdf_dir.clone()); - - config.build.output_filename = value.build.filename; - - config.diagnostics.allowed_patterns = value - .diagnostics - .allowed_patterns - .into_iter() - .map(|pattern| pattern.0) - .collect(); - - config.diagnostics.ignored_patterns = value - .diagnostics - .ignored_patterns - .into_iter() - .map(|pattern| pattern.0) - .collect(); - - config.diagnostics.delay = value - .diagnostics_delay - .map_or(config.diagnostics.delay, Duration::from_millis); - - 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, - LatexFormatter::Texlab => Formatter::Server, - LatexFormatter::Latexindent => Formatter::LatexIndent, - }; - - config.formatting.bib_formatter = match value.bibtex_formatter { - BibtexFormatter::None => Formatter::Null, - BibtexFormatter::Texlab => Formatter::Server, - BibtexFormatter::Latexindent => Formatter::LatexIndent, - }; - - config.formatting.line_length = - value - .formatter_line_length - .map_or(80, |len| if len < 0 { usize::MAX } else { len as usize }); - - config.formatting.latex_indent.local = value.latexindent.local; - config.formatting.latex_indent.modify_line_breaks = value.latexindent.modify_line_breaks; - - config.synctex = value - .forward_search - .executable - .zip(value.forward_search.args) - .map(|(program, args)| SynctexConfig { program, args }); - - config.symbols.allowed_patterns = value - .symbols - .allowed_patterns - .into_iter() - .map(|pattern| pattern.0) - .collect(); - - config.symbols.ignored_patterns = value - .symbols - .ignored_patterns - .into_iter() - .map(|pattern| pattern.0) - .collect(); - - config.inlay_hints.label_definitions = value.inlay_hints.label_definitions.unwrap_or(true); - config.inlay_hints.label_references = value.inlay_hints.label_references.unwrap_or(true); - - config.completion.matcher = match value.completion.matcher { - CompletionMatcher::Fuzzy => base_db::MatchingAlgo::Skim, - CompletionMatcher::FuzzyIgnoreCase => base_db::MatchingAlgo::SkimIgnoreCase, - CompletionMatcher::Prefix => base_db::MatchingAlgo::Prefix, - CompletionMatcher::PrefixIgnoreCase => base_db::MatchingAlgo::PrefixIgnoreCase, - }; - - config - .syntax - .math_environments - .extend(value.experimental.math_environments); - - config - .syntax - .enum_environments - .extend(value.experimental.enum_environments); - - config - .syntax - .verbatim_environments - .extend(value.experimental.verbatim_environments); - - config - .syntax - .citation_commands - .extend(value.experimental.citation_commands); - - config - .syntax - .label_reference_commands - .extend(value.experimental.label_reference_commands); - - config - } -} diff --git a/support/texlab/crates/texlab/src/util.rs b/support/texlab/crates/texlab/src/util.rs index c40ba8b51a..d9ec5a2fb1 100644 --- a/support/texlab/crates/texlab/src/util.rs +++ b/support/texlab/crates/texlab/src/util.rs @@ -22,7 +22,7 @@ pub fn normalize_uri(uri: &mut lsp_types::Url) { } fn fix_drive_letter(text: &str) -> Option { - if !text.is_ascii() { + if !text.is_ascii() || text.len() == 0 { return None; } diff --git a/support/texlab/crates/texlab/src/util/client_flags.rs b/support/texlab/crates/texlab/src/util/client_flags.rs index e5813e4f02..842dbb1eae 100644 --- a/support/texlab/crates/texlab/src/util/client_flags.rs +++ b/support/texlab/crates/texlab/src/util/client_flags.rs @@ -37,4 +37,7 @@ pub struct ClientFlags { /// If `true`, the server can report progress using `WorkDoneProgress`. pub progress: bool, + + /// If `true`, the server can let the client open a document using `window/showDocument`. + pub show_document: bool, } diff --git a/support/texlab/crates/texlab/src/util/from_proto.rs b/support/texlab/crates/texlab/src/util/from_proto.rs index e05d317dfe..db4f3a5b39 100644 --- a/support/texlab/crates/texlab/src/util/from_proto.rs +++ b/support/texlab/crates/texlab/src/util/from_proto.rs @@ -1,4 +1,6 @@ -use base_db::{FeatureParams, Workspace}; +use std::time::Duration; + +use base_db::{Config, FeatureParams, Formatter, SynctexConfig, Workspace}; use completion::CompletionParams; use definition::DefinitionParams; use highlights::HighlightParams; @@ -8,7 +10,10 @@ use references::ReferenceParams; use rename::RenameParams; use rowan::TextSize; -use crate::features::completion::ResolveInfo; +use crate::{ + features::completion::ResolveInfo, + server::options::{BibtexFormatter, CompletionMatcher, LatexFormatter, Options}, +}; use super::{line_index_ext::LineIndexExt, ClientFlags}; @@ -94,6 +99,12 @@ pub fn client_flags( .and_then(|cap| cap.work_done_progress) .unwrap_or(false); + let show_document = capabilities + .window + .as_ref() + .and_then(|cap| cap.show_document.as_ref()) + .map_or(false, |cap| cap.support); + ClientFlags { hierarchical_document_symbols, completion_markdown, @@ -106,6 +117,7 @@ pub fn client_flags( definition_link, folding_custom_kinds, progress, + show_document, } } @@ -221,3 +233,137 @@ pub fn completion_resolve_info(item: &mut lsp_types::CompletionItem) -> Option Config { + let mut config = Config::default(); + config.root_dir = value.root_directory; + + config.build.program = value.build.executable.unwrap_or(config.build.program); + config.build.args = value.build.args.unwrap_or(config.build.args); + config.build.on_save = value.build.on_save; + config.build.forward_search_after = value.build.forward_search_after; + + config.build.aux_dir = value + .build + .aux_directory + .or_else(|| value.aux_directory.clone()) + .unwrap_or_else(|| String::from(".")); + + config.build.pdf_dir = value + .build + .pdf_directory + .or(value.aux_directory) + .unwrap_or_else(|| String::from(".")); + + config.build.log_dir = value + .build + .log_directory + .unwrap_or_else(|| config.build.pdf_dir.clone()); + + config.build.output_filename = value.build.filename; + + config.diagnostics.allowed_patterns = value + .diagnostics + .allowed_patterns + .into_iter() + .map(|pattern| pattern.0) + .collect(); + + config.diagnostics.ignored_patterns = value + .diagnostics + .ignored_patterns + .into_iter() + .map(|pattern| pattern.0) + .collect(); + + config.diagnostics.delay = value + .diagnostics_delay + .map_or(config.diagnostics.delay, Duration::from_millis); + + 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, + LatexFormatter::Texlab => Formatter::Server, + LatexFormatter::Latexindent => Formatter::LatexIndent, + }; + + config.formatting.bib_formatter = match value.bibtex_formatter { + BibtexFormatter::None => Formatter::Null, + BibtexFormatter::Texlab => Formatter::Server, + BibtexFormatter::Latexindent => Formatter::LatexIndent, + }; + + config.formatting.line_length = + value + .formatter_line_length + .map_or(80, |len| if len < 0 { usize::MAX } else { len as usize }); + + config.formatting.latex_indent.local = value.latexindent.local; + config.formatting.latex_indent.modify_line_breaks = value.latexindent.modify_line_breaks; + + config.synctex = value + .forward_search + .executable + .zip(value.forward_search.args) + .map(|(program, args)| SynctexConfig { program, args }); + + config.symbols.allowed_patterns = value + .symbols + .allowed_patterns + .into_iter() + .map(|pattern| pattern.0) + .collect(); + + config.symbols.ignored_patterns = value + .symbols + .ignored_patterns + .into_iter() + .map(|pattern| pattern.0) + .collect(); + + config.inlay_hints.label_definitions = value.inlay_hints.label_definitions.unwrap_or(true); + config.inlay_hints.label_references = value.inlay_hints.label_references.unwrap_or(true); + + config.completion.matcher = match value.completion.matcher { + CompletionMatcher::Fuzzy => base_db::MatchingAlgo::Skim, + CompletionMatcher::FuzzyIgnoreCase => base_db::MatchingAlgo::SkimIgnoreCase, + CompletionMatcher::Prefix => base_db::MatchingAlgo::Prefix, + CompletionMatcher::PrefixIgnoreCase => base_db::MatchingAlgo::PrefixIgnoreCase, + }; + + config + .syntax + .math_environments + .extend(value.experimental.math_environments); + + config + .syntax + .enum_environments + .extend(value.experimental.enum_environments); + + config + .syntax + .verbatim_environments + .extend(value.experimental.verbatim_environments); + + config + .syntax + .citation_commands + .extend(value.experimental.citation_commands); + + config + .syntax + .label_definition_commands + .extend(value.experimental.label_definition_commands); + + config + .syntax + .label_reference_commands + .extend(value.experimental.label_reference_commands); + + config +} diff --git a/support/texlab/texlab.1 b/support/texlab/texlab.1 index 00148f341b..2d4e412dc9 100644 --- a/support/texlab/texlab.1 +++ b/support/texlab/texlab.1 @@ -1,12 +1,22 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH TEXLAB "1" "March 2024" "texlab 5.14.1" "User Commands" +.TH TEXLAB "1" "May 2024" "texlab 5.16.0" "User Commands" .SH NAME -texlab \- manual page for texlab 5.14.1 +texlab \- manual page for texlab 5.16.0 .SH SYNOPSIS .B texlab -[\fI\,OPTIONS\/\fR] +[\fI\,OPTIONS\/\fR] [\fI\,COMMAND\/\fR] .SH DESCRIPTION An implementation of the Language Server Protocol for LaTeX +.SS "Commands:" +.TP +run +Runs the language server in a editor context using STDIN and STDOUT +.TP +inverse\-search +Opens a document at a specific line +.TP +help +Print this message or the help of the given subcommand(s) .SH OPTIONS .TP \fB\-v\fR, \fB\-\-verbosity\fR... diff --git a/support/texlab/texlab.pdf b/support/texlab/texlab.pdf index 87a904383d..fe59b1f434 100644 Binary files a/support/texlab/texlab.pdf and b/support/texlab/texlab.pdf differ -- cgit v1.2.3